From c83b3df5925b5c037b7e61e135e2723d9c6b0749 Mon Sep 17 00:00:00 2001 From: Lpsd <40902730+Lpsd@users.noreply.github.com> Date: Tue, 3 Oct 2023 19:56:50 +0100 Subject: [PATCH] Update cURL from 8.1.2 to 8.3.0 (#3199) Re-introduction of 85e707b --- vendor/curl/.gitignore | 3 + vendor/curl/CHANGES | 11310 +++++++++---------- vendor/curl/RELEASE-NOTES | 418 +- vendor/curl/include/curl/curl.h | 41 +- vendor/curl/include/curl/curlver.h | 10 +- vendor/curl/include/curl/mprintf.h | 38 +- vendor/curl/include/curl/multi.h | 2 +- vendor/curl/include/curl/system.h | 65 +- vendor/curl/include/curl/typecheck-gcc.h | 1 + vendor/curl/include/curl/urlapi.h | 3 +- vendor/curl/include/curl/websockets.h | 12 +- vendor/curl/lib/altsvc.c | 87 +- vendor/curl/lib/amigaos.c | 1 + vendor/curl/lib/amigaos.h | 1 - vendor/curl/lib/asyn-ares.c | 16 +- vendor/curl/lib/asyn-thread.c | 8 +- vendor/curl/lib/base64.c | 11 +- vendor/curl/lib/bufq.c | 29 +- vendor/curl/lib/c-hyper.c | 123 +- vendor/curl/lib/c-hyper.h | 1 - vendor/curl/lib/cf-h1-proxy.c | 178 +- vendor/curl/lib/cf-h2-proxy.c | 811 +- vendor/curl/lib/cf-haproxy.c | 17 +- vendor/curl/lib/cf-https-connect.c | 50 +- vendor/curl/lib/cf-socket.c | 278 +- vendor/curl/lib/cfilters.c | 17 +- vendor/curl/lib/cfilters.h | 4 +- vendor/curl/lib/conncache.h | 3 +- vendor/curl/lib/connect.c | 72 +- vendor/curl/lib/cookie.c | 9 +- vendor/curl/lib/curl_base64.h | 9 +- vendor/curl/lib/curl_des.c | 11 +- vendor/curl/lib/curl_des.h | 11 +- vendor/curl/lib/curl_hmac.h | 3 +- vendor/curl/lib/curl_log.h | 138 - vendor/curl/lib/curl_md4.h | 9 +- vendor/curl/lib/curl_md5.h | 4 +- vendor/curl/lib/curl_memory.h | 58 +- vendor/curl/lib/curl_ntlm_core.c | 86 +- vendor/curl/lib/curl_ntlm_core.h | 9 - vendor/curl/lib/curl_printf.h | 1 + vendor/curl/lib/curl_sasl.c | 26 +- vendor/curl/lib/curl_setup.h | 28 +- vendor/curl/lib/curl_setup_once.h | 6 + vendor/curl/lib/curl_sha256.h | 4 +- vendor/curl/lib/{curl_log.c => curl_trc.c} | 87 +- vendor/curl/lib/curl_trc.h | 150 + vendor/curl/lib/dynbuf.h | 2 - vendor/curl/lib/easy.c | 50 +- vendor/curl/lib/easy_lock.h | 4 + vendor/curl/lib/easyoptions.c | 7 +- vendor/curl/lib/fopen.c | 14 +- vendor/curl/lib/formdata.c | 4 +- vendor/curl/lib/formdata.h | 7 +- vendor/curl/lib/ftp.c | 139 +- vendor/curl/lib/getinfo.c | 7 + vendor/curl/lib/gopher.c | 4 +- vendor/curl/lib/headers.c | 13 +- vendor/curl/lib/hmac.c | 5 +- vendor/curl/lib/hostip.c | 60 +- vendor/curl/lib/hsts.c | 5 +- vendor/curl/lib/http.c | 208 +- vendor/curl/lib/http.h | 18 +- vendor/curl/lib/http1.c | 131 +- vendor/curl/lib/http1.h | 4 +- vendor/curl/lib/http2.c | 804 +- vendor/curl/lib/http_aws_sigv4.c | 249 +- vendor/curl/lib/http_digest.c | 2 +- vendor/curl/lib/http_digest.h | 4 +- vendor/curl/lib/http_proxy.c | 16 +- vendor/curl/lib/idn.c | 120 +- vendor/curl/lib/idn.h | 6 +- vendor/curl/lib/if2ip.c | 4 + vendor/curl/lib/imap.c | 199 +- vendor/curl/lib/inet_ntop.c | 5 +- vendor/curl/lib/krb5.c | 15 +- vendor/curl/lib/ldap.c | 12 +- vendor/curl/lib/{vtls/nssg.h => macos.c} | 38 +- vendor/curl/lib/{vtls/gskit.h => macos.h} | 19 +- vendor/curl/lib/md4.c | 42 +- vendor/curl/lib/md5.c | 8 +- vendor/curl/lib/mime.c | 34 +- vendor/curl/lib/mqtt.c | 6 +- vendor/curl/lib/multi.c | 203 +- vendor/curl/lib/multiif.h | 5 + vendor/curl/lib/pingpong.c | 12 +- vendor/curl/lib/pop3.c | 57 +- vendor/curl/lib/rand.c | 3 +- vendor/curl/lib/rand.h | 14 - vendor/curl/lib/sendf.c | 83 +- vendor/curl/lib/sendf.h | 9 +- vendor/curl/lib/setopt.c | 28 +- vendor/curl/lib/setup-os400.h | 91 +- vendor/curl/lib/setup-vms.h | 1 - vendor/curl/lib/sha256.c | 21 +- vendor/curl/lib/smb.c | 205 +- vendor/curl/lib/smb.h | 197 - vendor/curl/lib/smtp.c | 47 +- vendor/curl/lib/socks.c | 19 +- vendor/curl/lib/strerror.c | 2 +- vendor/curl/lib/system_win32.h | 5 +- vendor/curl/lib/telnet.c | 13 +- vendor/curl/lib/timeval.c | 16 +- vendor/curl/lib/transfer.c | 38 +- vendor/curl/lib/url.c | 112 +- vendor/curl/lib/urlapi.c | 145 +- vendor/curl/lib/urldata.h | 91 +- vendor/curl/lib/vauth/cram.c | 4 +- vendor/curl/lib/vauth/digest.c | 6 +- vendor/curl/lib/vauth/digest.h | 2 +- vendor/curl/lib/vauth/digest_sspi.c | 4 +- vendor/curl/lib/vauth/ntlm.c | 13 +- vendor/curl/lib/vauth/vauth.h | 6 +- vendor/curl/lib/version.c | 4 +- vendor/curl/lib/vquic/curl_msh3.c | 65 +- vendor/curl/lib/vquic/curl_ngtcp2.c | 709 +- vendor/curl/lib/vquic/curl_quiche.c | 418 +- vendor/curl/lib/vquic/vquic.c | 88 +- vendor/curl/lib/vquic/vquic_int.h | 3 + vendor/curl/lib/vssh/libssh.c | 5 +- vendor/curl/lib/vssh/libssh2.c | 38 +- vendor/curl/lib/vssh/wolfssh.c | 4 +- vendor/curl/lib/vtls/bearssl.c | 121 +- vendor/curl/lib/vtls/gskit.c | 1327 --- vendor/curl/lib/vtls/gtls.c | 44 +- vendor/curl/lib/vtls/hostcheck.c | 5 +- vendor/curl/lib/vtls/mbedtls.c | 42 +- vendor/curl/lib/vtls/nss.c | 2522 ----- vendor/curl/lib/vtls/openssl.c | 161 +- vendor/curl/lib/vtls/rustls.c | 52 +- vendor/curl/lib/vtls/schannel.c | 138 +- vendor/curl/lib/vtls/schannel.h | 119 +- vendor/curl/lib/vtls/schannel_int.h | 194 + vendor/curl/lib/vtls/schannel_verify.c | 53 +- vendor/curl/lib/vtls/sectransp.c | 107 +- vendor/curl/lib/vtls/vtls.c | 86 +- vendor/curl/lib/vtls/vtls.h | 2 +- vendor/curl/lib/vtls/vtls_int.h | 5 +- vendor/curl/lib/vtls/wolfssl.c | 231 +- vendor/curl/lib/vtls/x509asn1.c | 21 +- vendor/curl/lib/vtls/x509asn1.h | 7 +- vendor/curl/lib/warnless.c | 10 +- vendor/curl/lib/warnless.h | 10 +- vendor/curl/lib/ws.c | 56 +- vendor/curl/premake5.lua | 3 +- 145 files changed, 11291 insertions(+), 13335 deletions(-) delete mode 100644 vendor/curl/lib/curl_log.h rename vendor/curl/lib/{curl_log.c => curl_trc.c} (77%) create mode 100644 vendor/curl/lib/curl_trc.h rename vendor/curl/lib/{vtls/nssg.h => macos.c} (57%) rename vendor/curl/lib/{vtls/gskit.h => macos.h} (79%) delete mode 100644 vendor/curl/lib/vtls/gskit.c delete mode 100644 vendor/curl/lib/vtls/nss.c create mode 100644 vendor/curl/lib/vtls/schannel_int.h diff --git a/vendor/curl/.gitignore b/vendor/curl/.gitignore index 9b46b54f07..80336c9b12 100644 --- a/vendor/curl/.gitignore +++ b/vendor/curl/.gitignore @@ -1,5 +1,8 @@ * +!**/ !.gitignore !premake5.lua !include/ !lib/ +!**.c +!**.h diff --git a/vendor/curl/CHANGES b/vendor/curl/CHANGES index 2dfd4f3f0b..1c60df1b03 100644 --- a/vendor/curl/CHANGES +++ b/vendor/curl/CHANGES @@ -6,9240 +6,8978 @@ Changelog -Version 8.1.2 (30 May 2023) +Version 8.3.0 (13 Sep 2023) -Daniel Stenberg (30 May 2023) +Daniel Stenberg (13 Sep 2023) -- RELEASE-NOTES: synced +- RELEASE-NOTES: syn ced - 8.1.2 release + curl 8.3.0 release -- THANKS: contributors from 8.1.2 +- THANKS: contributors from 8.3.0 -- lib1560: verify more scheme guessing +Thorsten Klein (12 Sep 2023) - - on 2nd level domains - - on names without dots +- cmake: set SIZEOF_LONG_LONG in curl_config.h - As mentioned in #11161, "imap.com" will be guessed IMAP + in order to support 32bit builds regarding wolfssl CTC_SETTINGS - Closes #11219 + Closes #11839 -- page-header: minor wording polish in the URL segment +Jay Satiro (12 Sep 2023) - Closes #11217 +- curl_ngtcp2: fix error message -- page-header: mention curl version and how to figure out current release +- http_aws_sigv4: handle no-value user header entries - Closes #11216 + - Handle user headers in format 'name:' and 'name;' with no value. -- RELEASE-NOTES: synced + The former is used when the user wants to remove an internal libcurl + header and the latter is used when the user actually wants to send a + no-value header in the format 'name:' (note the semi-colon is converted + by libcurl to a colon). -- configure: without pkg-config and no custom path, use -lnghttp2 + Prior to this change the AWS header import code did not special case + either of those and the generated AWS SignedHeaders would be incorrect. - Reported-by: correctmost on github - Fixes #11186 - Closes #11210 + Reported-by: apparentorder@users.noreply.github.com -Stefan Eissing (28 May 2023) + Ref: https://curl.se/docs/manpage.html#-H -- curl: cache the --trace-time value for a second + Fixes https://github.com/curl/curl/issues/11664 + Closes https://github.com/curl/curl/pull/11668 - - caches HH:MM:SS computed and reuses it for logging during - the same second. - - common function for plain log line start formatting +Dan Fandrich (11 Sep 2023) - Closes #11211 +- CI: run pytest with the -v option -Kev Jackson (28 May 2023) + This lists of the test cases being run so it can be tracked over time. -- libcurl.m4: remove trailing 'dnl' that causes this to break autoconf + Closes #11824 - Closes #11212 +Daniel Stenberg (11 Sep 2023) -Stefan Eissing (26 May 2023) +- HTTP3: the msquic backend is not functional -- http3: send EOF indicator early as possible + I ask that we do not submit bugs for this backend just yet as we know it + does not fully work. - - ngtcp2 and quiche implementations relied on the DONE_SEND event - to forward the EOF for uploads to the libraries. This often - result in a last 0 length EOF data. Tracking the amount of - data left to upload allows EOF indication earlier. - - refs #11205 where CloudFlare DoH servers did not like to - receive the initial upload DATA without EOF and returned - a 400 Bad Request + Closes #11831 + Closes #11819 - Reported-by: Sergey Fionov - Fixes #11205 - Closes #11207 +- aws_sigv4: the query canon code miscounted URL encoded input -Daniel Stenberg (26 May 2023) + Added some extra ampersands to test 439 to verify "blank" query parts -- scripts/contri*sh: no longer grep -v ' ' + Follow-up to fc76a24c53b08cdf - Originally these scripts filtered out names that have no space so that - they better avoid nick names not intended for credits. Such names are - not too commonly used, plus we now give credit even to those. + Closes #11829 - Additionally: non-latin names, like Asian, don't have spaces at all so - they were also filtered out and had to be manually added which made it - an error-prone operation where Asian names eventually easily fell off by - mistake. +vvb2060 (11 Sep 2023) - Closes #11206 +- quic: don't set SNI if hostname is an IP address -- cf-socket: restore Curl_sock_assign_addr() + We already do this for TLS connections. - Regression since it was not private. Also used by msh3.c + RFC 6066 says: Literal IPv4 and IPv6 addresses are not permitted in + "HostName". - Follow-up to 8e85764b7bd7f05f5 - Reported-by: Gisle Vanem - Fixes #11202 - Closes #11204 + Ref: https://www.rfc-editor.org/rfc/rfc6066#section-3 + + Fixes https://github.com/curl/curl/issues/11827 + Closes https://github.com/curl/curl/pull/11828 + +Daniel Stenberg (10 Sep 2023) - RELEASE-NOTES: synced - Taken down to 8.1.2 now for pending patch release +Benoit Pierre (10 Sep 2023) -- libssh: when keyboard-interactive auth fails, try password +- configure: fix `HAVE_TIME_T_UNSIGNED` check - The state machine had a mistake in that it would not carry on to that - next step. + The syntax was incorrect (need a proper main body), and the test + condition was wrong (resulting in a signed `time_t` detected as + unsigned). - This also adds a verbose output what methods that are available from the - server and renames the macros that change to the next auth methods to - try. + Closes #11825 - Reported-by: 左潇峰 - Fixes #11196 - Closes #11197 +Daniel Stenberg (9 Sep 2023) -Emanuele Torre (25 May 2023) +- THANKS-filter: pszlazak on github -- configure: fix build with arbitrary CC and LD_LIBRARY_PATH +pszlazak (9 Sep 2023) - Since ./configure and processes that inherit its environment variables - are the only callers of the run-compiler script, we can just save the - current value of the LD_LIBRARY_PATH and CC variables to another pair of - environment variables, and make run-compiler a static script that - simply restores CC and LD_LIBRARY_PATH to the saved value, and before - running the compiler. +- include.d: explain headers not printed with --fail before 7.75.0 - This avoids having to inject the values of the variables in the script, - possibly causing problems if they contains spaces, quotes, and other - special characters. + Prior to 7.75.0 response headers were not printed if -f/--fail was used + and an error was reported by server. This was fixed in ab525c0 + (precedes 7.75.0). - Also add exports in the script just in case LD_LIBRARY_PATH and CC are - not already in the environment. + Closes #11822 - follow-up from 471dab2 +Daniel Stenberg (8 Sep 2023) - Closes #11182 +- http_aws_sigv4: skip the op if the query pair is zero bytes -Daniel Stenberg (25 May 2023) + Follow-up to fc76a24c53b08cdf -- urlapi: remove superfluous host name check + Spotted by OSS-Fuzz - ... as it is checked later more proper. + Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=62175 + Closes #11823 - Closes #11195 +- cmdline-docs: use present tense, not future -Stefan Eissing (25 May 2023) + + some smaller cleanups -- http2: fix EOF handling on uploads with auth negotiation + Closes #11821 - - doing a POST with `--digest` does an override on the initial request - with `Content-Length: 0`, but the http2 filter was unaware of that - and expected the originally request body. It did therefore not - send a final DATA frame with EOF flag to the server. - - The fix overrides any initial notion of post size when the `done_send` - event is triggered by the transfer loop, leading to the EOF that - is necessary. - - refs #11194. The fault did not happen in testing, as Apache httpd - never tries to read the request body of the initial request, - sends the 401 reply and closes the stream. The server used in the - reported issue however tried to read the EOF and timed out on the - request. +- cmdline-docs: make sure to phrase it as "added in ...." - Reported-by: Aleksander Mazur - Fixes #11194 - Cloes #11200 + References to things that were added or changed in a specific version + should be specified as "(added in [version]) for two reasons: -Daniel Stenberg (23 May 2023) + 1 - consistency -- RELEASE-NOTES: synced + 2 - to allow gen.pl to strip them out if deemed referring to too old + versions - bump to 8.2.0 + Closes #11821 -- lib: remove unused functions, make single-use static +Jay Satiro (8 Sep 2023) - Closes #11174 +- docs: mark --ssl-revoke-best-effort as Schannel specific -- scripts/singleuse.pl: add more API calls + Closes https://github.com/curl/curl/pull/11760 -Christian Hesse (23 May 2023) +Nathan Moinvaziri (8 Sep 2023) -- configure: quote the assignments for run-compiler +- schannel: fix ordering of cert chain info - Building for multilib failed, as the compiler command contains an - extra argument. That needs quoting. + - Use CERT_CONTEXT's pbCertEncoded to determine chain order. - Regression from b78ca50cb3dda361f9c1 + CERT_CONTEXT from SECPKG_ATTR_REMOTE_CERT_CONTEXT contains + end-entity/server certificate in pbCertEncoded. We can use this pointer + to determine the order of certificates when enumerating hCertStore using + CertEnumCertificatesInStore. - Fixes #11179 - Closes #11180 + This change is to help ensure that the ordering of the certificate chain + requested by the user via CURLINFO_CERTINFO has the same ordering on all + versions of Windows. -Daniel Stenberg (23 May 2023) + Prior to this change Schannel certificate order was reversed in 8986df80 + but that was later reverted in f540a39b when it was discovered that + Windows 11 22H2 does the reversal on its own. -- misc: fix spelling mistakes + Ref: https://github.com/curl/curl/issues/9706 - Reported-by: musvaage on github - Fixes #11171 - Closes #11172 + Closes https://github.com/curl/curl/pull/11632 -Version 8.1.1 (23 May 2023) +Chris Talbot (8 Sep 2023) -Daniel Stenberg (23 May 2023) +- digest: Use hostname to generate spn instead of realm -- RELEASE-NOTES: synced + In https://www.rfc-editor.org/rfc/rfc2831#section-2.1.2 - curl 8.1.1 + digest-uri-value should be serv-type "/" host , where host is: -- THANKS: contributors from the 8.1.1 release + The DNS host name or IP address for the service requested. The + DNS host name must be the fully-qualified canonical name of the + host. The DNS host name is the preferred form; see notes on server + processing of the digest-uri. -Dan Fandrich (22 May 2023) + Realm may not be the host, so we must specify the host explicitly. -- docs: fix fuzzing documentation link + Note this change only affects the non-SSPI digest code. The digest code + used by SSPI builds already uses the hostname to generate the spn. - Follow-up to 4c712a1b + Ref: https://github.com/curl/curl/issues/11369 -- CI: add an Alpine build with MUSL + Closes https://github.com/curl/curl/pull/11395 - MUSL is another libc implementation which has its own unique issues - worth testing. +Daniel Stenberg (7 Sep 2023) - Ref: #11140 - Closes #11178 +- docs: remove use of the word 'very' -- runtests: add a missing \n at the end of a log message + It is mostly superfluous. proselint would complain. -correctmost on github (22 May 2023) + Closes #11818 -- SECURITY-PROCESS.md: link security advisory doc and fix typo +- curl_multi_remove_handle.3: clarify what happens with connection - Closes #11177 + Closes #11817 -Daniel Stenberg (22 May 2023) +- RELEASE-NOTES: synced -- TODO: build curl with Windows Unicode support +- test439: verify query canonization for aws-sigv4 - Closes #7229 +- tool_operate: make aws-sigv4 not require TLS to be used -- KNOWN_BUGS: hyper memory-leaks + Maybe not used too often, but we want it for testing and it should work. - Closes #10803 +- http_aws_sigv4: canonicalize the query -Stefan Eissing (22 May 2023) + Percent encoding needs to be done using uppercase, and most + non-alphanumerical must be percent-encoded. -- http/2: unstick uploads + Fixes #11794 + Reported-by: John Walker + Closes #11806 - - refs #11157 and #11175 where uploads get stuck or lead to RST streams - - fixes our h2 send behaviour to continue sending in the nghttp2 session - as long as it wants to. This will empty our send buffer as long as - the remote stream/connection window allows. - - in case the window is exhausted, the data remaining in the send buffer - will wait for a WINDOW_UPDATE from the server. Which is a socket event - that engages our transfer loop again - - the problem in the issue was that we did not exhaust the window, but - left data in the sendbuffer and no further socket events did happen. - The server was just waiting for us to send more. - - relatedly, there was an issue fixed that closing a stream with KEEP_HOLD - set kept the transfer from shutting down - as it should have - leading - to a timeout. +Wyatt O'Day (7 Sep 2023) - Closes #11176 +- lib: add ability to disable auths individually -Daniel Stenberg (21 May 2023) + Both with configure and cmake -- workflows/macos: add a job using gcc + debug + secure transport + Closes #11490 -Jay Satiro (21 May 2023) +Stefan Eissing (7 Sep 2023) -- lib: fix conversion warnings with gcc on macOS +- ngtcp2: fix handling of large requests -Daniel Stenberg (21 May 2023) + - requests >64K are send in parts to the filter + - fix parsing of the request to assemble it correctly + from several sends + - open a QUIC stream only when the complete request has + been collected -- sectransp.c: make the code c89 compatible + Closes #11815 - Follow-up to dd2bb485521c2ec713001b3a +- openssl: when CURLOPT_SSL_CTX_FUNCTION is registered, init x509 store before - Reported-by: FeignClaims on github - Fixes #11155 - Closes #11159 + - we delay loading the x509 store to shorten the handshake time. + However an application callback installed via CURLOPT_SSL_CTX_FUNCTION + may need to have the store loaded and try to manipulate it. + - load the x509 store before invoking the app callback -Emanuele Torre (21 May 2023) + Fixes #11800 + Reported-by: guoxinvmware on github + Cloes #11805 -- Revert "urlapi: respect CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY for redirect - s" +Daniel Stenberg (7 Sep 2023) - This reverts commit df6c2f7b544f1f35f2a3e0be11f345affeb6fe9c. - (It only keep the test case that checks redirection to an absolute URL - without hostname and CURLU_NO_AUTHORITY). +- krb5: fix "implicit conversion loses integer precision" warnings - I originally wanted to make CURLU_ALLOW_SPACE accept spaces in the - hostname only because I thought - curl_url_set(CURLUPART_URL, CURLU_ALLOW_SPACE) was already accepting - them, and they were only not being accepted in the hostname when - curl_url_set(CURLUPART_URL) was used for a redirection. + conversions to/from enum and unsigned chars - That is not actually the case, urlapi never accepted hostnames with - spaces, and a hostname with a space in it never makes sense. - I probably misread the output of my original test when I they were - normally accepted when using CURLU_ALLOW_SPACE, and not redirecting. + Closes #11814 - Some other URL parsers seems to allow space in the host part of the URL, - e.g. both python3's urllib.parse module, and Chromium's javascript URL - object allow spaces (chromium percent escapes the spaces with %20), - (they also both ignore TABs, and other whitespace characters), but those - URLs with spaces in the hostname are useless, neither python3's requests - module nor Chromium's window.location can actually use them. +Stefan Eissing (7 Sep 2023) - There is no reason to add support for URLs with spaces in the host, - since it was not a inconsistency bug; let's revert that patch before it - makes it into release. Sorry about that. +- pytest: improvements - I also reverted the extra check for CURLU_NO_AUTHORITY since that does - not seem to be necessary, CURLU_NO_AUTHORITY already worked for - redirects. + - set CURL_CI for pytest runs in CI environments + - exclude timing sensitive tests from CI runs + - for failed results, list only the log and stat of + the failed transfer - Closes #11169 + - fix type in http.c comment -Dan Fandrich (20 May 2023) + Closes #11812 -- runtests: use the correct fd after select +- CI: move on to ngtcp2 v0.19.1 - The code was using the wrong fd when determining which runner was ready - with a response. + Closes #11809 - Ref: #10818 - Closes #11160 +Dan Fandrich (5 Sep 2023) -- test425: fix the log directory for the upload +- CI: run Circle macOS builds on x86 for now - This must be %LOGDIR to let it work with parallel tests. + The ARM machines aren't ready for us and requesting them now causes + warnings e-mails to be sent to some PR pushers. - Ref: #10969 + Ref: #11771 -- runtests: handle interrupted reads from IPC pipes +Viktor Szakats (5 Sep 2023) - These can be interrupted by signals, especially SIGINT to shut down, and - must be restarted so the IPC call arrives correctly. If the read just - returns an error instead, the IPC calling state will go out of sync and - a proper shutdown won't happen. +- http3: adjust cast for ngtcp2 v0.19.0 - Ref: #10818 + ngtcp2 v0.19.0 made size of `ecn` member of `ngtcp2_pkt_info` + an `uint8_t` (was: `uint32_t`). Adjust our local cast accordingly. -Stefan Eissing (20 May 2023) + Fixes: + ``` + ./curl/lib/vquic/curl_ngtcp2.c:1912:12: warning: implicit conversion loses in + teger precision: 'uint32_t' (aka 'unsigned int') to 'uint8_t' (aka 'unsigned + char') [-Wimplicit-int-conversion] + pi.ecn = (uint32_t)ecn; + ~ ^~~~~~~~~~~~~ + ``` -- http2: upload improvements + Also bump ngtcp2, nghttp3 and nghttp2 to their latest versions in our + docs and CI. - Make send buffer smaller to have progress and "upload done" reporting - closer to reality. Fix handling of send "drain" condition to no longer - trigger once the transfer loop reports it is done sending. Also do not - trigger the send "drain" on RST streams. + Ref: https://github.com/ngtcp2/ngtcp2/commit/80447281bbc94af53f8aa7a4cfc19175 + 782894a3 + Ref: https://github.com/ngtcp2/ngtcp2/pull/877 + Closes #11798 - Background: - - a upload stall was reported in #11157 that timed out - - test_07_33a reproduces a problem with such a stall if the - server 404s the request and RSTs the stream. - - test_07_33b verifies a successful PUT, using the parameters - from #11157 and checks success +Stefan Eissing (5 Sep 2023) - Ref: #11157 - Closes #11165 +- http: fix sending of large requests -- http2: increase stream window size to 10 MB + - refs #11342 where errors with git https interactions + were observed + - problem was caused by 1st sends of size larger than 64KB + which resulted in later retries of 64KB only + - limit sending of 1st block to 64KB + - adjust h2/h3 filters to cope with parsing the HTTP/1.1 + formatted request in chunks - Reported-by: pandada8 on github + - introducing Curl_nwrite() as companion to Curl_write() + for the many cases where the sockindex is already known - Fixes #11162 - Closes #11167 + Fixes #11342 (again) + Closes #11803 -Daniel Stenberg (20 May 2023) +- pytest: fix check for slow_network skips to only apply when intended -- lib: rename struct 'http_req' to 'httpreq' + Closes #11801 - Because FreeBSD 14 kidnapped the name. - Ref: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271526 +Daniel Stenberg (5 Sep 2023) - Fixes #11163 - Closes #11164 +- curl_url_get/set.3: add missing semicolon in SYNOPSIS -Viktor Szakats (20 May 2023) +- CURLOPT_URL.3: explain curl_url_set() uses the same parser -- cmake: avoid `list(PREPEND)` for compatibility +- CURLOPT_URL.3: add two URL API calls in the see-also section - `list(PREPEND)` requires CMake v3.15, our minimum is v3.7. +Dan Fandrich (4 Sep 2023) - Ref: https://cmake.org/cmake/help/latest/command/list.html#prepend +- CI: add a 32-bit i686 Linux build - Regression from 1e3319a167d2f32d295603167486e9e88af9bb4e + This is done by cross-compiling under regular x86_64 Linux. Since the + kernel offers backwards compatibility, the binaries can be tested as + normal. - Reported-by: Keitagit-kun on Github - Fixes #11141 - Closes #11144 + Closes #11799 -Daniel Stenberg (19 May 2023) +- tests: fix a type warning on 32-bit x86 -- RELEASE-NOTES: synced +Viktor Szakats (4 Sep 2023) -Stefan Eissing (19 May 2023) +- tests: delete stray `.orig` file -- ngtcp2: proper handling of uint64_t when adjusting send buffer + Follow-up to 331b89a319d0067fa1e6441719307cfef9c7960f + Closes #11797 - Fixes #11149 - Closes #11153 +Daniel Stenberg (4 Sep 2023) -- ngtcp2: fix compiler warning about possible null-deref +- RELEASE-NOTES: synced - - compiler analyzer did not include the call context for this - static function where the condition had already been checked. - - eleminating the problem by making stream a call parameter +Viktor Szakats (4 Sep 2023) - Fixes #11147 - Closes #11151 +- lib: silence compiler warning in inet_ntop6 -Emanuele Torre (19 May 2023) + ``` + ./curl/lib/inet_ntop.c:121:21: warning: possible misuse of comma operator her + e [-Wcomma] + cur.base = i, cur.len = 1; + ^ + ./curl/lib/inet_ntop.c:121:9: note: cast expression to void to silence warnin + g + cur.base = i, cur.len = 1; + ^~~~~~~~~~~~ + (void)( ) + ``` -- docs: document that curl_url_cleanup(NULL) is a safe no-op + Closes #11790 - This has always been the case, but it was not documented. +Daniel Stenberg (4 Sep 2023) - The paragraph was copied verbatim from curl_easy_cleanup.3 +- transfer: also stop the sending on closed connection - Closes #11150 + Previously this cleared the receiving bit only but in some cases it is + also still sending (like a request-body) when disconnected and neither + direction can continue then. -Antoine Pitrou (19 May 2023) + Fixes #11769 + Reported-by: Oleg Jukovec + Closes #11795 -- select: avoid returning an error on EINTR from select() or poll() +John Bampton (4 Sep 2023) - This was already done for the poll() and select() calls - made directly from Curl_poll(), but was missed in - Curl_wait_ms(), which is called when there are no fds - to wait on. +- docs: change `sub-domain` to `subdomain` - Fixes #11135 - Closes #11143 + https://en.wikipedia.org/wiki/Subdomain -Daniel Stenberg (19 May 2023) + Closes #11793 -- vquic.c: make recvfrom_packets static, avoid compiler warning +Stefan Eissing (4 Sep 2023) - warning: no previous prototype for 'recvfrom_packets' +- multi: more efficient pollfd count for poll - Reported-by: Keitagit-kun on github - Fixes #11146 - Closes #11148 + - do not use separate pollfds for sockets that have POLLIN+POLLOUT -- urlapi: allow numerical parts in the host name + Closes #11792 - It can only be an IPv4 address if all parts are all digits and no more than - four parts, otherwise it is a host name. Even slightly wrong IPv4 will now be - passed through as a host name. +- http2: polish things around POST - Regression from 17a15d88467 shipped in 8.1.0 + - added test cases for various code paths + - fixed handling of blocked write when stream had + been closed inbetween attempts + - re-enabled DEBUGASSERT on send with smaller data size - Extended test 1560 accordingly. + - in debug builds, environment variables can be set to simulate a slow + network when sending data. cf-socket.c and vquic.c support + * CURL_DBG_SOCK_WBLOCK: percentage of send() calls that should be + answered with a EAGAIN. TCP/UNIX sockets. + This is chosen randomly. + * CURL_DBG_SOCK_WPARTIAL: percentage of data that shall be written + to the network. TCP/UNIX sockets. + Example: 80 means a send with 1000 bytes would only send 800 + This is applied to every send. + * CURL_DBG_QUIC_WBLOCK: percentage of send() calls that should be + answered with EAGAIN. QUIC only. + This is chosen randomly. - Reported-by: Pavel Kalyugin - Fixes #11129 - Closes #11131 + Closes #11756 -Emilio Cobos Álvarez (19 May 2023) +Daniel Stenberg (4 Sep 2023) -- http2: double http request parser max line length +- docs: add curl_global_trace to some SEE ALSO sections - This works around #11138, by doubling the limit, and should be a - relatively safe fix. + Closes #11791 - Ideally the buffer would grow as needed and there would be no need for a - limit? But that might be follow-up material. +- os400: fix checksrc nits - Fixes #11138 - Closes #11139 + Closes #11789 -Emanuele Torre (18 May 2023) +Nicholas Nethercote (3 Sep 2023) -- configure: fix --help alignment +- hyper: remove `hyptransfer->endtask` - AC_ARG_ENABLE seems to only trim off whitespace from the start and end - of its help-string argument, while prepending two spaces of indentation - to all lines. + `Curl_hyper_stream` needs to distinguish between two kinds of + `HYPER_TASK_EMPTY` tasks: (a) the `foreach` tasks it creates itself, and + (b) background tasks that hyper produces. It does this by recording the + address of any `foreach` task in `hyptransfer->endtask` before pushing + it into the executor, and then comparing that against the address of + tasks later polled out of the executor. - This means that the two spaces of indentation between the --enable-rtsp - and the --disable-rtsp line were not removed causing ./configure --help - to print: + This works right now, but there is no guarantee from hyper that the + addresses are stable. `hyper_executor_push` says "The executor takes + ownership of the task, which should not be accessed again unless + returned back to the user with `hyper_executor_poll`". That wording is a + bit ambiguous but with my Rust programmer's hat on I read it as meaning + the task returned with `hyper_executor_poll` may be conceptually the + same as a task that was pushed, but that there are no other guarantees + and comparing addresses is a bad idea. - Optional Features: - [...] - --enable-rtsp Enable RTSP support - --disable-rtsp Disable RTSP support + This commit instead uses `hyper_task_set_userdata` to mark the `foreach` + task with a `USERDATA_RESP_BODY` value which can then be checked for, + removing the need for `hyptransfer->endtask`. This makes the code look + more like that hyper C API examples, which use userdata for every task + and never look at task addresses. - I removed the indentation to fix the issue, now it prints: + Closes #11779 - Optional Features: - [...] - --enable-rtsp Enable RTSP support - --disable-rtsp Disable RTSP support +Dave Cottlehuber (3 Sep 2023) - The --enable-hsts and --disable-hsts lines had the same problems, and - have been fixed too. +- ws: fix spelling mistakes in examples and tests - Closes #11142 + Closes #11784 -Deal(一线灵) (18 May 2023) +Daniel Stenberg (3 Sep 2023) -- cmake: repair cross compiling +- tool_filetime: make -z work with file dates before 1970 - It cannot *run* code for testing purposes when cross-compiling. + Fixes #11785 + Reported-by: Harry Sintonen + Closes #11786 - Closes #11130 +Dan Fandrich (1 Sep 2023) -Daniel Stenberg (18 May 2023) +- build: fix portability of mancheck and checksrc targets -- configure: generate a script to run the compiler + At least FreeBSD preserves cwd across makefile lines, so rules + consisting of more than one "cd X; do_something" must be explicitly run + in a subshell to avoid this. This problem caused the Cirrus FreeBSD + build to fail when parallel make jobs were enabled. - in the CURL_RUN_IFELSE macro, with LD_LIBRARY_PATH set to the value of - the configure invoke, and not the value that might be used later, - intended for the execution of the output the compiler ouputs. +- CI: adjust labeler match patterns for new & obsolete files - For example when the compiler uses the same library (like libz) that - configure checks for. +- configure: trust pkg-config when it's used for zlib - Reported-by: Jonas Bülow - Fixes #11114 - Closes #11120 + The library flags retrieved from pkg-config were later thrown out and + harded-coded, which negates the whole reason to use pkg-config. + Also, previously, the assumption was made that --libs-only-l and + --libs-only-L are the full decomposition of --libs, which is untrue and + would not allow linking against a static zlib. The new approach is + better in that it uses --libs, although only if --libs-only-l returns + nothing. -Stefan Eissing (18 May 2023) + Bug: https://curl.se/mail/lib-2023-08/0081.html + Reported-by: Randall + Closes #11778 -- cf-socket: completely remove the disabled USE_RECV_BEFORE_SEND_WORKAROUND +Stefan Eissing (1 Sep 2023) - Closes #11118 +- CI/ngtcp2: clear wolfssl for when cache is ignored -Emanuele Torre (18 May 2023) + Closes #11783 -- urlapi: respect CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY for redirects +Daniel Stenberg (1 Sep 2023) - curl_url_set(uh, CURLUPART_URL, redirurl, flags) was not respecing - CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY in the host part of redirurl - when redirecting to an absolute URL. +- RELEASE-NOTES: synced - Closes #11136 +Nicholas Nethercote (1 Sep 2023) -Colin Cross (18 May 2023) +- hyper: fix a progress upload counter bug -- hostip: move easy_lock.h include above curl_memory.h + `Curl_pgrsSetUploadCounter` should be a passed a total count, not an + increment. - Similar to #9561, move easy_lock.h above curl_memory.h to fix building - against musl libc. + This changes the failing diff for test 579 with hyper from this: + ``` + Progress callback called with UL 0 out of 0[LF] + -Progress callback called with UL 8 out of 0[LF] + -Progress callback called with UL 16 out of 0[LF] + -Progress callback called with UL 26 out of 0[LF] + -Progress callback called with UL 61 out of 0[LF] + -Progress callback called with UL 66 out of 0[LF] + +Progress callback called with UL 29 out of 0[LF] + ``` + to this: + ``` + Progress callback called with UL 0 out of 0[LF] + -Progress callback called with UL 8 out of 0[LF] + -Progress callback called with UL 16 out of 0[LF] + -Progress callback called with UL 26 out of 0[LF] + -Progress callback called with UL 61 out of 0[LF] + -Progress callback called with UL 66 out of 0[LF] + +Progress callback called with UL 40 out of 0[LF] + ``` + Presumably a step in the right direction. - Closes #11140 + Closes #11780 -Hind Montassif (18 May 2023) +Daniel Stenberg (1 Sep 2023) -- curl_easy_getinfo: clarify on return data types +- awssiv4: avoid freeing the date pointer on error - Closes #11126 + Since it was not allocated, don't free it even if it was wrong syntax -Emanuele Torre (18 May 2023) + Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=61908 -- checksrc: disallow spaces before labels + Follow-up to b137634ba3adb - Out of 415 labels throughout the code base, 86 of those labels were - not at the start of the line. Which means labels always at the start of - the line is the favoured style overall with 329 instances. + Closes #11782 - Out of the 86 labels not at the start of the line: - * 75 were indented with the same indentation level of the following line - * 8 were indented with exactly one space - * 2 were indented with one fewer indentation level then the following - line - * 1 was indented with the indentation level of the following line minus - three space (probably unintentional) +Stefan Eissing (1 Sep 2023) - Co-Authored-By: Viktor Szakats +- CI: ngtcp2-linux: use separate caches for tls libraries - Closes #11134 + allow ever changing master for wolfssl -Daniel Stenberg (18 May 2023) + Closes #11766 -- cookie: update the comment on cookie length and size limits +- replace `master` as wolfssl-version with recent commit - To refer to the proper cookie RFC and the upcoming RFC refresh. +- wolfssl, use master again in CI - Closes #11127 + - with the shared session update fix landed in master, it + is time to use that in our CI again -- url: provide better error message when URLs fail to parse +Nicholas Nethercote (31 Aug 2023) - By providing the URL API error message into the error message. +- tests: fix formatting errors in `FILEFORMAT.md`. - Ref: #11129 - Closes #11137 + Without the surrounding backticks, these tags get swallowed when the + markdown is rendered. -- RELEASE-NOTES: synced + Closes #11777 - bumped to 8.1.1 +Viktor Szakats (31 Aug 2023) -Jon Rumsey (18 May 2023) +- cmake: add support for `CURL_DEFAULT_SSL_BACKEND` -- os400: update chkstrings.c + Allow overriding the default TLS backend via a CMake setting. - Compensate changes for recent changes to urldata.h to reclassify - STRING_AWS_SIGV4. + E.g.: + `cmake [...] -DCURL_DEFAULT_SSL_BACKEND=mbedtls` - Fixes #11132 - Closes #11133 + Accepted values: bearssl, gnutls, mbedtls, openssl, rustls, + schannel, secure-transport, wolfssl -Version 8.1.0 (17 May 2023) + The passed string is baked into the curl/libcurl binaries. + The value is case-insensitive. -Daniel Stenberg (17 May 2023) + We added a similar option to autotools in 2017 via + c7170e20d0a18ec8a514b4daa53bcdbb4dcb3a05. -- RELEASE-NOTES: synced + TODO: Convert to lowercase to improve reproducibility. -- THANKS: contributors from the 8.1.0 release + Closes #11774 -- hostip: include easy_lock.h before using GLOBAL_INIT_IS_THREADSAFE +- sectransp: fix compiler warnings - Since that header file is the only place that define can be defined. + https://github.com/curl/curl-for-win/actions/runs/6037489221/job/16381860220# + step:3:11046 + ``` + /Users/runner/work/curl-for-win/curl-for-win/curl/lib/vtls/sectransp.c:2435:1 + 4: warning: unused variable 'success' [-Wunused-variable] + OSStatus success; + ^ + /Users/runner/work/curl-for-win/curl-for-win/curl/lib/vtls/sectransp.c:3300:4 + 4: warning: unused parameter 'sha256len' [-Wunused-parameter] + size_t sha256len) + ^ + ``` - Reported-by: Marc Deslauriers + Closes #11773 - Follow-up to 13718030ad4b3209 +- tidy-up: mostly whitespace nits - Closes #11121 + - delete completed TODO from `./CMakeLists.txt`. + - convert a C++ comment to C89 in `./CMake/CurlTests.c`. + - delete duplicate EOLs from EOF. + - add missing EOL at EOF. + - delete whitespace at EOL (except from expected test results). + - convert tabs to spaces. + - convert CRLF EOLs to LF in GHA yaml. + - text casing fixes in `./CMakeLists.txt`. + - fix a codespell typo in `packages/OS400/initscript.sh`. -Thomas Taylor (16 May 2023) + Closes #11772 -- aws-sigv4.d: fix region identifier in example +Dan Fandrich (31 Aug 2023) - Closes #11117 +- CI: remove Windows builds from Cirrus, without replacement -Philip Heiduck (15 May 2023) + If we don't do this, all coverage on Cirrus will cease in a few days. By + removing the Windows builds, the FreeBSD one should still continue + as before. The Windows builds will need be moved to another service to + maintain test coverage. -- mlc_config.json: remove this linkcheck CI job config file + Closes #11771 - Closes #11113 +- CI: switch macOS ARM build from Cirrus to Circle CI -Daniel Silverstone (15 May 2023) + Cirrus is drastically reducing their free tier on Sept. 1, so they will + no longer perform all these builds for us. All but one build has been + moved, with the LibreSSL one being dropped because of linking problems + on Circle. -- ssh: Add support for libssh2 read timeout + One important note about this change is that Circle CI is currently + directing all these builds to x86_64 hardware, despite them requesting + ARM. This is because ARM nodes are scheduled to be available on the + free tier only in December. This reduces our architectural diversity + until then but it should automatically come back once those machines are + enabled. - Hook the new (1.11.0 or newer) libssh2 support for setting a read timeout - into the SERVER_RESPONSE_TIMEOUT option. With this done, clients can use - the standard curl response timeout setting to also control the time that - libssh2 will wait for packets from a slow server. This is necessary to - enable use of very slow SFTP servers. +- CI: use the right variable for BSD make - Signed-off-by: Daniel Silverstone + BSD uses MAKEFLAGS instead of MAKE_FLAGS so it wasn't doing parallel + builds before. - Closes #10965 +- CI: drop the FreeBSD 12.X build -Osama Albahrani (14 May 2023) + Cirrus' new free tier won't let us have many builds, so drop the + nonessential ones. The FreeBSD 13.X build will still give us the most + relevant FreeBSD coverage. -- GIT-INFO: add --with-openssl +- CI: move the Alpine build from Cirrus to GHA - Closes #11110 + Cirrus is reducing their free tier to next to nothing, so we must move + builds elsewhere. -Daniel Stenberg (13 May 2023) +Stefan Eissing (30 Aug 2023) -- RELEASE-NOTES: synced +- test_07_upload.py: fix test_07_34 curl args -Marcel Raad (13 May 2023) + - Pass correct filename to --data-binary. -- md(4|5): don't use deprecated iOS functions + Prior to this change --data-binary was passed an incorrect filename due + to a missing separator in the arguments list. Since aacbeae7 curl will + error on incorrect filenames for POST. - They are marked as deprecated in iOS 13.0, which might result in - warnings-as-errors. + Fixes https://github.com/curl/curl/issues/11761 + Closes https://github.com/curl/curl/pull/11763 - Also, use `*_MIN_REQUIRED` instead of `*_MIN_ALLOWED`, which seems to - be what's currently used. +Nicholas Nethercote (30 Aug 2023) - Bug: https://github.com/curl/curl/issues/11098 - Closes https://github.com/curl/curl/pull/11102 +- tests: document which tests fail due to hyper's lack of trailer support. -- md4: only build when used + Closes #11762 - Its only usage in curl_ntlm_core.c is guarded by `USE_CURL_NTLM_CORE`, - so let's use this here too. +- docs: removing "pausing transfers" from HYPER.md. - Ref: https://github.com/curl/curl/issues/11098 - Closes https://github.com/curl/curl/pull/11102 + It's a reference to #8600, which was fixed by #9070. -Vítor Galvão (12 May 2023) + Closes #11764 -- write-out.d: Use response_code in example +Patrick Monnerat (30 Aug 2023) - Closes #11107 +- os400: handle CURL_TEMP_PRINTF() while building bind source -Shohei Maeda (12 May 2023) + Closes #11547 -- url: fix null dispname for --connect-to option +- os400: build test servers - Closes #11106 + Also fix a non-compliant main prototype in disabled.c. -Daniel Stenberg (12 May 2023) + Closes #11547 -- test2306: verify getting a second response with folded headers +- tests: fix compilation error for os400 - Reproduces the isue #11101 and verifies the fix. + OS400 uses BSD 4.3 setsockopt() prototype by default: this does not + define parameter as const, resulting in an error if actual parameter is + const. Remove the const keyword from the actual parameter cast: this + works in all conditions, even if the formal parameter uses it. - Verifies a17b2a503f + Closes #11547 -- headers: clear (possibly) lingering pointer in init +- os400: make programs and command name configurable - The "prevhead" pointer is used for the headers storage but was not - cleared correctly in init, which made it possible to act up when a - handle is reused. + Closes #11547 - Reported-by: Steve Herrell - Fixes #11101 - Closes #11103 +- os400: move build configuration parameters to a separate script -- RELEASE-NOTES: synced + They can then easily be overriden in a script named "config400.override" + that is not part of the distribution. -- ngtcp2: use 0.15.0 + Closes #11547 - - nghttp3 0.11.0 - - nghttp2 1.53.0 +- os400: implement CLI tool - Adapt to new API calls + This is provided as a QADRT (ascii) program, a link to it in the IFS and + a minimal CL command. - Closes #11031 + Closes #11547 -Jay Satiro (10 May 2023) +Matthias Gatto (30 Aug 2023) -- openssl: fix indent +- lib: fix aws-sigv4 having date header twice in some cases -Daniel Stenberg (10 May 2023) + When the user was providing the header X-XXX-Date, the header was + re-added during signature computation, and we had it twice in the + request. -- CURLOPT_DNS_CACHE_TIMEOUT.3: fix spelling + Reported-by: apparentorder@users.noreply.github.com - Follow-up to 9ed7d56e044f5aa1b29 + Signed-off-by: Matthias Gatto - Closes #11096 + Fixes: https://github.com/curl/curl/issues/11738 + Closes: https://github.com/curl/curl/pull/11754 -- hostip: use time_t for storing oldest DNS entry +Jay Satiro (30 Aug 2023) - Theoretically, the oldest time could overflow an int. In practice that - won't happen, but let's do this to please analyzers. +- multi: remove 'processing: ' debug message - Follow-up to 9ed7d56e044f5aa1b2928ccde6245d0 + - Remove debug message added by e024d566. - Pointed out by Coverity. - Closes #11094 + Closes https://github.com/curl/curl/pull/11759 -- http: free the url before storing a new copy +- ftp: fix temp write of ipv6 address - To avoid a memory-leak. + - During the check to differentiate between a port and IPv6 address + without brackets, write the binary IPv6 address to an in6_addr. - Reported-by: Hiroki Kurosawa + Prior to this change the binary IPv6 address was erroneously written to + a sockaddr_in6 'sa6' when it should have been written to its in6_addr + member 'sin6_addr'. There's no fallout because no members of 'sa6' are + accessed before it is later overwritten. - Closes #11093 + Closes https://github.com/curl/curl/pull/11747 -- compressed.d: clarify the words on "not notifying headers" +- tool: change some fopen failures from warnings to errors - Reported-by: Dylan Anthony - Fixes #11091 - Closes #11092 + - Error on missing input file for --data, --data-binary, + --data-urlencode, --header, --variable, --write-out. -- libssh2: free fingerprint better + Prior to this change if a user of the curl tool specified an input file + for one of the above options and that file could not be opened then it + would be treated as zero length data instead of an error. For example, a + POST using `--data @filenametypo` would cause a zero length POST which + is probably not what the user intended. - Reported-by: Wei Chong Tan - Closes #11088 + Closes https://github.com/curl/curl/pull/11677 -- CURLOPT_IPRESOLVE.3: clarify that this for host names, not IP addresses +- hostip: fix typo - Reported-by: Harry Sintonen - Closes #11087 +Davide Masserut (29 Aug 2023) -- hostip: enforce a maximum DNS cache size independent of timeout value +- tool: avoid including leading spaces in the Location hyperlink - To reduce the damage an application can cause if using -1 or other - ridiculous timeout values and letting the cache live long times. + Co-authored-by: Dan Fandrich - The maximum number of entries in the DNS cache is now totally - arbitrarily and hard-coded set to 29999. + Closes #11735 - Closes #11084 +Daniel Stenberg (29 Aug 2023) -- hostip: store dns timeout as 'int' +- SECURITY-PROCESS.md: not a sec issue: Tricking user to run a cmdline - ... because it set and held as an 'int' elsewhere and can never be - larger. + Closes #11757 -- RELEASE-NOTES: synced +- connect: stop halving the remaining timeout when less than 600 ms left -- tool_operate: refuse (--data or --form) and --continue-at combo + When curl wants to connect to a host, it always has a TIMEOUT. The + maximum time it is allowed to spend until a connect is confirmed. - libcurl assumes that a --continue-at resumption is done to continue an - upload using the read callback and neither --data nor --form use - that and thus won't do what the user wants. Whatever the user wants - with this strange combination. + curl will try to connect to each of the IP adresses returned for the + host. Two loops, one for each IP family. - Add test 426 to verify. + During the connect loop, while curl has more than one IP address left to + try within a single address family, curl has traditionally allowed (time + left/2) for *this* connect attempt. This, to not get stuck on the + initial addresses in case the timeout but still allow later addresses to + get attempted. - Reported-by: Smackd0wn on github - Fixes #11081 - Closes #11083 + This has the downside that when users set a very short timeout and the + host has a large number of IP addresses, the effective result might be + that every attempt gets a little too short time. -- transfer: refuse POSTFIELDS + RESUME_FROM combo + This change stop doing the divided-by-two if the total time left is + below a threshold. This threshold is 600 milliseconds. - The code assumes that such a resume is wanting to continue an upload - using the read callback, and since POSTFIELDS is done without callback - libcurl will just misbehave. + Closes #11693 - This combo will make the transfer fail with CURLE_BAD_FUNCTION_ARGUMENT - with an explanation in the error message. +- asyn-ares: reduce timeout to 2000ms - Reported-by: Smackd0wn on github - Fixes #11081 - Closes #11083 + When UDP packets get lost this makes for slightly faster retries. This + lower timeout is used by @c-ares itself by default starting next + release. -- ipv4.d/ipv6.d: they are "mutex", not "boolean" + Closes #11753 - ... which for example means they do not have --no-* versions. +John Bampton (29 Aug 2023) - Reported-by: Harry Sintonen - Fixes #11085 - Closes #11086 +- misc: remove duplicate words -- docs/SECURITY-ADVISORY.md: how to write a curl security advisory + Closes #11740 - Closes #11080 +Daniel Stenberg (29 Aug 2023) -nobedee on github (5 May 2023) +- RELEASE-NOTES: synced -- MANUAL.md: add dict example for looking up a single definition +- wolfSSL: avoid the OpenSSL compat API when not needed - Closes #11077 + ... and instead call wolfSSL functions directly. -Dan Fandrich (5 May 2023) + Closes #11752 -- runtests: fix -c option when run with valgrind +Viktor Szakats (28 Aug 2023) - The curl binary argument wasn't being quoted properly. This seems to - have broken at some point after quoting was added in commit 606b29fe. +- lib: fix null ptr derefs and uninitialized vars (h2/h3) - Reported-by: Daniel Stenberg - Ref: #11073 - Fixes #11074 - Closes #11076 + Fixing compiler warnings with gcc 13.2.0 in unity builds. -- runtests: support creating more than one runner process + Assisted-by: Jay Satiro + Assisted-by: Stefan Eissing + Closes #11739 - The controller currently only creates and uses one, but more are now - possible. +Jay Satiro (28 Aug 2023) - Ref: #10818 +- secureserver.pl: fix stunnel version parsing -- runtests: spawn a new process for the test runner + - Allow the stunnel minor-version version part to be zero. - When the -j option is given, a new process is spawned in which the test - programs are run and from which test servers are started. Only one - process can be started at once, but this is sufficient to test that the - infrastructure can isolate those functions in a new task. There should - be no visible difference between the two modes at the moment. + Prior to this change with the stunnel version scheme of . + if either part was 0 then version parsing would fail, causing + secureserver.pl to fail with error "No stunnel", causing tests that use + the SSL protocol to be skipped. As a practical matter this bug can only + be caused by a minor-version part of 0, since the major-version part is + always greater than 0. - Ref: #10818 - Closes #11064 + Closes https://github.com/curl/curl/pull/11722 -- runtests: turn singletest() into a state machine +- secureserver.pl: fix stunnel path quoting - This allows it to run in a non-blocking manner. + - Store the stunnel path in the private variable $stunnel unquoted and + instead quote it in the command strings. - Ref: #10818 + Prior to this change the quoted stunnel path was passed to perl's file + operators which cannot handle quoted paths. For example: -- runtests: change runner interface to be asynchronous + $stunnel = "\"/C/Program Files (x86)/stunnel/bin/tstunnel\""; + if(-x $stunnel or -x "$stunnel") + # false even if path exists and is executable - Program arguments are marshalled and then written to the end of a pipe - which is later read from and the arguments unmarshalled before the - desired function is called normally. The function return values are - then marshalled and written into another pipe when is later read from - and unmarshalled before being returned to the caller. + Our other test scripts written in perl, unlike this one, use servers.pm + which has a global $stunnel variable with the path stored unquoted and + therefore those scripts don't have this problem. - The implementation is currently blocking but can be made non-blocking - without any changes to the API. This allows calling multiple runners - without blocking in the future. + Closes https://github.com/curl/curl/pull/11721 - Ref: #10818 +Daniel Stenberg (28 Aug 2023) -- runtests: call citest_finishtest in singletest +- altsvc: accept and parse IPv6 addresses in response headers - This is where citest_starttest is called. + Store numerical IPv6 addresses in the alt-svc file with the brackets + present. - Ref: #10818 + Verify with test 437 and 438 -- runtests: add a runner initialization function + Fixes #11737 + Reported-by: oliverpool on github + Closes #11743 - This sets up the runner environment to start running tests. +- libtest: use curl_free() to free libcurl allocated data - Ref: #10818 + In several test programs. These mistakes are not detected or a problem + as long as memdebug.h is included, as that provides the debug wrappers + for all memory functions in the same style libcurl internals do it, + which makes curl_free and free effectively the same call. -- runtests: remove directory from server filename variables + Reported-by: Nicholas Nethercote + Closes #11746 - There will soon be multiple log directories so the paths will no longer - be static in runtests.pl. Also, get rid of $SERVER2IN which was not - used. +Jay Satiro (28 Aug 2023) - Ref: #10818 +- disable.d: explain --disable not implemented prior to 7.50.0 -- runtests: reduce package exports after refactoring + Option -q/--disable was added in 5.0 but only -q was actually + implemented. Later --disable was implemented in e200034 (precedes + 7.49.0), but incorrectly, and fixed in 6dbc23c (precedes 7.50.0). - Some recent refactoring made these export no longer necessary. Also, - stop displaying the Unix socket paths at startup since there will soon - be many of them and they're not that interesting. + Reported-by: pszlazak@users.noreply.github.com - Ref: #10818 + Fixes https://github.com/curl/curl/issues/11710 + Closes #11712 -- runtests: use a function to obtain $LOGDIR for a test +Nicholas Nethercote (28 Aug 2023) - This will no longer be static soon. +- hyper: fix ownership problems - Ref: #10818 + Some of these changes come from comparing `Curl_http` and + `start_CONNECT`, which are similar, and adding things to them that are + present in one and missing in another. -Jay Satiro (5 May 2023) + The most important changes: + - In `start_CONNECT`, add a missing `hyper_clientconn_free` call on the + happy path. + - In `start_CONNECT`, add a missing `hyper_request_free` on the error + path. + - In `bodysend`, add a missing `hyper_body_free` on an early-exit path. + - In `bodysend`, remove an unnecessary `hyper_body_free` on a different + error path that would cause a double-free. + https://docs.rs/hyper/latest/hyper/ffi/fn.hyper_request_set_body.html + says of `hyper_request_set_body`: "This takes ownership of the + hyper_body *, you must not use it or free it after setting it on the + request." This is true even if `hyper_request_set_body` returns an + error; I confirmed this by looking at the hyper source code. -- tool_cb_hdr: Fix 'Location:' formatting for early VTE terminals + Other changes are minor but make things slightly nicer. - - Disable hyperlink formatting for the 'Location:' header value in VTE - 0.48.1 and earlier, since it is buggy in some of those versions. + Closes #11745 - Prior to this change those terminals may show the location header value - as gibberish or show it twice. +Daniel Stenberg (28 Aug 2023) - Ref: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#backw - ard-compatibility +- multi.h: the 'revents' field of curl_waitfd is supported - Fixes https://github.com/curl/curl/issues/10428 - Closes https://github.com/curl/curl/pull/11071 + Since 6d30f8ebed34e7276 -François Michel (3 May 2023) + Reported-by: Nicolás Ojeda Bär + Ref: #11748 + Closes #11749 -- quiche: disable pacing while pacing is not actually performed +Gerome Fournier (27 Aug 2023) - Closes #11068 +- tool_paramhlp: improve str2num(): avoid unnecessary call to strlen() -Daniel Stenberg (2 May 2023) + Closes #11742 -- easy_cleanup: require a "good" handle to act +Daniel Stenberg (27 Aug 2023) - By insisting that the passed in handle is "good" (the magic number is - intact), this can limit the potential damage if a bad pointer is passed - in. Like when this function is called twice on the same handle pointer. +- docs: mention critical files in same directories as curl saves - Ref: #10964 - Closes #11061 + ... cannot be fully protected. Don't do it. -Andreas Falkenhahn (1 May 2023) + Co-authored-by: Jay Satiro + Reported-by: Harry Sintonen + Fixes #11530 + Closes #11701 -- amiga: Fix CA certificate paths for AmiSSL and MorphOS +John Hawthorn (26 Aug 2023) - AmiSSL stores certificates in `AmiSSL:Certs` and MorphOS stores them in - `MOSSYS:Data/SSL/curl-ca-bundle.crt`. +- OpenSSL: clear error queue after SSL_shutdown - Closes https://github.com/curl/curl/pull/11059 + We've seen errors left in the OpenSSL error queue (specifically, + "shutdown while in init") by adding some logging it revealed that the + source was this file. -Daniel Stenberg (30 Apr 2023) + Since we call SSL_read and SSL_shutdown here, but don't check the return + code for an error, we should clear the OpenSSL error queue in case one + was raised. -- http2: (void)-mark when we explicitly ignore the return code + This didn't affect curl because we call ERR_clear_error before every + write operation (a0dd9df9ab35528eb9eb669e741a5df4b1fb833c), but when + libcurl is used in a process with other OpenSSL users, they may detect + an OpenSSL error pushed by libcurl's SSL_shutdown as if it was their + own. - When h2_progress_egress() is called. Pointed out by Coverity. + Co-authored-by: Satana de Sant'Ana - Closes #11057 + Closes #11736 -- checksrc: find bad indentation in conditions without open brace +Alexander Kanavin (25 Aug 2023) - If the previous line starts with if/while/for AND ends with a closed - parenthesis and there's an equal number of open and closed parentheses - on that line, verify that this line is indented $indent more steps, if - not a cpp line. +- tests: update cookie expiry dates to far in the future - Also adjust the fall-out from this fix. + This allows testing Y2038 with system time set to after that, so that + actual Y2038 issues can be exposed, and not masked by expiry errors. - Closes #11054 + Fixes #11576 + Closes #11610 -Diogo Teles Sant'Anna (28 Apr 2023) +John Bampton (25 Aug 2023) -- CI: Set minimal permissions on workflow ngtcp2-quictls.yml +- misc: fix spelling - Signed-off-by: Diogo Teles Sant'Anna + Closes #11733 - Closes #11055 +Daniel Stenberg (25 Aug 2023) -Dan Fandrich (28 Apr 2023) +- cmdline-opts/page-header: clarify stronger that !opt == URL -- CI: use another glob syntax for matching files on Appveyor + Everything provided on the command line that is not an option (or an + argument to an option) is treated as a URL. - The previous globbing syntax was not matching files recursively in - directories, so try appending a /* to more closely match the examples at - https://www.appveyor.com/docs/how-to/filtering-commits/ + Closes #11734 -Daniel Stenberg (28 Apr 2023) +- tests/runner: fix %else handling -- multi: add multi-ignore logic to multi_socket_action + Getting the show state proper for %else and %endif did not properly work + in nested cases. - The multi-ignore logic that was previously applied to - curl_multi_perform() (#10750) is here applied to the loop within - curl_multi_socket_action() to make it use the same optimization: most - handles have the same signal-ignore option state so this drastically - reduces the number of ignore/unignore calls per libcurl function invoke. + Follow-up to 3d089c41ea9 - Follow-up to bc90308328afb8 + Closes #11731 - Closes #11045 +Nicholas Nethercote (25 Aug 2023) -Stefan Eissing (28 Apr 2023) +- docs: Remove mention of #10803 from `KNOWN_BUGS`. -- http2: do flow window accounting for cancelled streams + Because the leaks have been fixed. - - nghttp2 does not free connection level window flow for - aborted streams - - when closing transfers, make sure that any buffered - response data is "given back" to the flow control window - - add tests test_02_22 and test_02_23 to reproduce +- c-hyper: fix another memory leak in `Curl_http`. - Closes #11052 + There is a `hyper_clientconn_free` call on the happy path, but not one + on the error path. This commit adds one. -- pingpong: fix compiler warning "assigning an enum to unsigned char" + Fixes the second memory leak reported by Valgrind in #10803. - Closes #11050 + Fixes #10803 + Closes #11729 -Daniel Stenberg (28 Apr 2023) +- c-hyper: fix a memory leak in `Curl_http`. -- configure: fix detection of apxs (for httpd) + A request created with `hyper_request_new` must be consumed by either + `hyper_clientconn_send` or `hyper_request_free`. - The condition check was turned the wrong way around! + This is not terrifically clear from the hyper docs -- + `hyper_request_free` is documented only with "Free an HTTP request if + not going to send it on a client" -- but a perusal of the hyper code + confirms it. - Closes #11051 + This commit adds a `hyper_request_free` to the `error:` path in + `Curl_http` so that the request is consumed when an error occurs after + the request is created but before it is sent. -Viktor Szakats (28 Apr 2023) + Fixes the first memory leak reported by Valgrind in #10803. -- ci: `-Wno-vla` no longer necessary + Closes #11729 - We handle this issue in the source now. +Daniel Stenberg (25 Aug 2023) - Follow-up to b725fe1944b45406676ea3aff333ae3085a848d9 +- RELEASE-NOTES: synced - Reviewed-by: Marcel Raad - Reviewed-by: Daniel Stenberg - Closes #11048 +John Bampton (25 Aug 2023) -Marcel Raad (28 Apr 2023) +- misc: spellfixes -- tests/http: make curl_setup.h the first include + Closes #11730 - This is required for the macros there to take effect for system - libraries. Specifically, including the system libraries first led to - warnings about `_FILE_OFFSET_BITS` being redefined in curl_config.h on - the Solaris autobuilds for ws-data.c and ws-pingpong.c. - Also make the curl includes come first for the other source files here - for consistency. +Daniel Stenberg (25 Aug 2023) - Closes https://github.com/curl/curl/pull/11046 +- tests: add support for nested %if conditions -Emanuele Torre (27 Apr 2023) + Provides more flexiblity to test cases. -- checksrc: check for spaces before the colon of switch labels + Also warn and bail out if there is an '%else' or %endif' without a + preceeding '%if'. - Closes #11047 + Ref: #11610 + Closes #11728 -Daniel Stenberg (27 Apr 2023) +- time-cond.d: mention what happens on a missing file -- RELEASE-NOTES: synced + Closes #11727 -- libssh: tell it to use SFTP non-blocking +Christian Hesse (24 Aug 2023) - Reported-by: Andreas Huebner - Fixes #11020 - Closes #11039 +- docs/cmdline-opts: match the current output -Stefan Eissing (27 Apr 2023) + The release date has been added in output, reflect that in documentation. -- http2: enlarge the connection window + Closes #11723 - - fixes stalled connections +Daniel Stenberg (24 Aug 2023) - - Make the connection window large enough, so that there is - some room left should 99/100 streams be PAUSED by the application +- lib: minor comment corrections - Reported-by: Paweł Wegner - Fixes #10988 - Closes #11043 +- docs: rewrite to present tense -Daniel Stenberg (27 Apr 2023) + ... instead of using future tense. -- checksrc: fix SPACEBEFOREPAREN for conditions starting with "*" + + numerous cleanups and improvements + + stick to "reuse" not "re-use" + + fewer contractions - The open paren check wants to warn for spaces before open parenthesis - for if/while/for but also for any function call. In order to avoid - catching function pointer declarations, the logic allows a space if the - first character after the open parenthesis is an asterisk. + Closes #11713 - I also spotted what we did not include "switch" in the check but we should. +- urlapi: setting a blank URL ("") is not an ok URL - This check is a little lame, but we reduce this problem by not allowing - that space for if/while/for/switch. + Test it in 1560 + Fixes #11714 + Reported-by: ad0p on github + Closes #11715 - Reported-by: Emanuele Torre - Closes #11044 +- spelling: use 'reuse' not 're-use' in code and elsewhere -- docs: minor polish + Unify the spelling as both versions were previously used intermittently - - "an HTTP*" (not "a") - - remove a few contractions - - remove a spurious "a" - - reduce use of "I" in texts + Closes #11717 - Closes #11040 +Michael Osipov (23 Aug 2023) -- ws: fix CONT opcode check +- system.h: add CURL_OFF_T definitions on HP-UX with HP aCC - Detected by Coverity. Follow-up to 930c00c259 + HP-UX on IA64 provides two modes: 32 and 64 bit while 32 bit being the + default one. Use "long long" in 32 bit mode and just "long" in 64 bit + mode. - Closes #11037 + Closes #11718 -Dan Fandrich (27 Apr 2023) +Dan Fandrich (22 Aug 2023) -- CI: switch the awslc builds to build out-of-tree +- tests: don't call HTTP errors OK in test cases - This is a common configuration that should be tested to avoid - regressions. The awsls cmake build was already out-of-tree so the - automake build now joins it. + Some HTTP errors codes were accompanied by the text OK, which causes + some cognitive dissonance when reading them. - Ref: #11006 +- http: close the connection after a late 417 is received -- tests/http: fix out-of-tree builds + In this situation, only part of the data has been sent before aborting + so the connection is no longer usable. - Add both lib/ directories (src & build) to the search path so - curl_setup.h and its dependencies can be found. + Assisted-by: Jay Satiro + Fixes #11678 + Closes #11679 - Followup-to acd82c8b +- runtests: slightly increase the longest log file displayed - Ref: #11006 - Closes #11036 + The new limit provides enough space for a 64 KiB data block to be logged + in a trace file, plus a few lines at the start and end for context. This + happens to be the amount of data sent at a time in a PUT request. -Daniel Stenberg (27 Apr 2023) +- tests: add delay command to the HTTP server -- urlapi: make internal function start with Curl_ + This adds a delay after client connect. - Curl_url_set_authority() it is. +Daniel Stenberg (22 Aug 2023) - Follow-up to acd82c8bfd +- cirrus: install everthing with pkg, avoid pip - Closes #11035 + Assisted-by: Sevan Janiyan -YX Hao (26 Apr 2023) + Closes #11711 -- cf-socket: turn off IPV6_V6ONLY on Windows if it is supported +- curl_url*.3: update function descriptions - IPV6_V6ONLY refs: - https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses - https://github.com/golang/go/blob/master/src/net/ipsock_posix.go - https://en.wikipedia.org/wiki/Unix-like - https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-o - ptions + - expand and clarify several descriptions + - avoid using future tense all over - default value refs: - https://datatracker.ietf.org/doc/html/rfc3493#section-5.3 - https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html#proc-sys-net - -ipv6-variables + Closes #11708 - Closes #10975 +- RELEASE-NOTES: synced -Daniel Stenberg (26 Apr 2023) +Stefan Eissing (21 Aug 2023) -- urldata: shrink *select_bits int => unsigned char +- CI/cirrus: disable python install on FreeBSD - - dselect_bits - - cselect_bits + - python cryptography package does not build build FreeBSD + - install just mentions "error" + - this gets the build and the main test suite going again - ... are using less than 8 bits. Changed types and moved them towards - the end of the structs to fit better. + Closes #11705 - Closes #11025 +- test2600: fix flakiness on low cpu -Stefan Eissing (26 Apr 2023) + - refs #11355 where failures to to low cpu resources in CI + are reported + - vastly extend CURLOPT_CONNECTTIMEOUT_MS and max durations + to test cases + - trigger Curl_expire() in test filter to allow re-checks before + the usual 1second interval -- tests/http: more tests with specific clients + Closes #11690 - - Makefile support for building test specific clients in tests/http/clients - - auto-make of clients when invoking pytest - - added test_09_02 for server PUSH_PROMISEs using clients/h2-serverpush - - added test_02_21 for lib based downloads and pausing/unpausing transfers +Maksim Sciepanienka (20 Aug 2023) - curl url parser: - - added internal method `curl_url_set_authority()` for setting the - authority part of a url (used for PUSH_PROMISE) +- tool_urlglob: use the correct format specifier for curl_off_t in msnprintf - http2: - - made logging of PUSH_PROMISE handling nicer + Closes #11698 - Placing python test requirements in requirements.txt files - - separate files to base test suite and http tests since use - and module lists differ - - using the files in the gh workflows +Daniel Stenberg (20 Aug 2023) - websocket test cases, fixes for we and bufq - - bufq: account for spare chunks in space calculation - - bufq: reset chunks that are skipped empty - - ws: correctly encode frames with 126 bytes payload - - ws: update frame meta information on first call of collect - callback that fills user buffer - - test client ws-data: some test/reporting improvements +- test687/688: two more basic --xattr tests - Closes #11006 + Closes #11697 -Jay Satiro (26 Apr 2023) +- cmdline-opts/docs: mentioned the negative option part -- libssh2: fix crash in keyboard callback + ... for --no-alpn and --no-buffer in the same style done for other --no- + options: - - Always set the libssh2 'abstract' user-pointer to the libcurl easy - handle associated with the ssh session, so it is always passed to the - ssh keyboard callback. + "Note that this is the negated option name documented." - Prior to this change and since 8b5f100 (precedes curl 8.0.0), if libcurl - was built without CURL_DEBUG then it could crash during the ssh auth - phase due to a null dereference in the ssh keyboard callback. + Closes #11695 - Reported-by: Andreas Falkenhahn +Emanuele Torre (19 Aug 2023) - Fixes https://github.com/curl/curl/pull/11024 - Closes https://github.com/curl/curl/pull/11026 +- tool/var: also error when expansion result starts with NUL -Daniel Stenberg (26 Apr 2023) + Expansions whose output starts with NUL were being expanded to the empty + string, and not being recognised as values that contain a NUL byte, and + should error. -- docs: clarify that more backends have HTTPS proxy support + Closes #11694 - Closes #11033 +Daniel Stenberg (19 Aug 2023) -- KNOWN_BUGS: remove two not-bugs +- tests: add 'large-time' as a testable feature - - 11.7 signal-based resolver timeouts + This allows test cases to require this feature to run and to be used in + %if conditions. - Not considered a bug anymore but just implementation details. People - should avoid using timeouts with the synchronous name resolver. + Large here means larger than 32 bits. Ie does not suffer from y2038. - - 11.16 libcurl uses renames instead of locking for atomic operations + Closes #11696 - Not a bug, just a description of how it works +- tests/Makefile: add check-translatable-options.pl to tarball - Closes #11032 + Used in test 1544 -Harry Sintonen (26 Apr 2023) + Follow-up to ae806395abc8c -- hostip: add locks around use of global buffer for alarm() +- gen.pl: fix a long version generation mistake - When building with the sync name resolver and timeout ability we now - require thread-safety to be present to enable it. + Too excessive escaping made the parsing not find the correct long names + later and instead add "wrong" links. - Closes #11030 + Follow-up to 439ff2052e219 -Daniel Stenberg (26 Apr 2023) + Reported-by: Lukas Tribus + Fixes #11688 + Closes #11689 -- curl_path: bring back support for SFTP path ending in /~ +- lib: move mimepost data from ->req.p.http to ->state - libcurl used to do a directory listing for this case (even though the - documentation says a URL needs to end in a slash for this), but - 4e2b52b5f7a3 modified the behavior. + When the legacy CURLOPT_HTTPPOST option is used, it gets converted into + the modem mimpost struct at first use. This data is (now) kept for the + entire transfer and not only per single HTTP request. This re-enables + rewind in the beginning of the second request instead of in end of the + first, as brought by 1b39731. - This change brings back a directory listing for SFTP paths that are - specified exactly as /~ in the URL. + The request struct is per-request data only. - Reported-by: Pavel Mayorov - Fixes #11001 - Closes #11023 + Extend test 650 to verify. -Emanuele Torre (26 Apr 2023) + Fixes #11680 + Reported-by: yushicheng7788 on github + Closes #11682 -- docs/libcurl/curl_*escape.3: rename "url" argument to "input"/"string" +Patrick Monnerat (17 Aug 2023) - Also reword the DESCRIPTION section to mention "input"/"string" argument - in bold. +- os400: do not check translatable options at build time - Closes #11027 + Now that there is a test for this, the build time check is not needed + anymore. -- docs/libcurl: minor cleanups + Closes #11650 - I was reading curl_unescape(3) and I noticed that there was an extra - space after the open parenthesis in the SYNOPSIS; I removed the extra - space. +- test1554: check translatable string options in OS400 wrapper - I also ran a few grep -r commands to find and remove extra spaces - after '(' in other files, and to find and replace uses of `T*' instead - of `T *'. Some of the instances of `T*` where unnecessary casts that I - removed. + This test runs a perl script that checks all string options are properly + translated by the OS400 character code conversion wrapper. It also + verifies these options are listed in alphanumeric order in the wrapper + switch statement. - I also fixed a comment that was misaligned in CURLMOPT_SOCKETFUNCTION.3. + Closes #11650 - And I fixed some formatting inconsistencies: in curl_unescape(3), all - function parameter were mentioned with bold text except length, that was - mentioned as 'length'; and, in curl_easy_unescape(3), all parameters - were mentioned in bold text except url that was italicised. Now they are - all mentioned in bold. - Documentation is not very consistent in how function parameter are - formatted: many pages italicise them, and others display them in bold - text; but I think it makes sense to at least be consistent with - formatting within the same page. +Daniel Stenberg (17 Aug 2023) - Closes #11027 +- unit3200: skip testing if function is not present -Daniel Stenberg (26 Apr 2023) + Fake a successful run since we have no easy mechanism to skip this test + for this advanced condition. -- man pages: simplify the .TH sections +- unit2600: fix build warning if built without verbose messages - - remove the version numbers - - simplify the texts +- test1608: make it build and get skipped without shuffle DNS support - The date and version number will be put there for releases when maketgz - runs the updatemanpages.pl script. +- lib: --disable-bindlocal builds curl without local binding support - Closes #11029 +- test1304: build and skip without netrc support -- hostcheck: fix host name wildcard checking +- lib: build fixups when built with most things disabled - The leftmost "label" of the host name can now only match against single - '*'. Like the browsers have worked for a long time. + Closes #11687 - - extended unit test 1397 for this - - move some SOURCE variables from unit/Makefile.am to unit/Makefile.inc +- workflows/macos.yml: disable zstd and alt-svc in the http-only build - Reported-by: Hiroki Kurosawa - Closes #11018 + Closes #11683 -Dan Fandrich (25 Apr 2023) +Stefan Eissing (17 Aug 2023) -- smbserver: remove temporary files before exit +- bearssl: handshake fix, provide proper get_select_socks() implementation - Each execution of test 1451 would leave a file in /tmp before. Since - Windows can't delete a file while it's open, all the temporary file - names are stored and deleted on exit. + - bring bearssl handshake times down from +200ms down to other TLS backends + - vtls: improve generic get_select_socks() implementation + - tests: provide Apache with a suitable ssl session cache - Closes #10990 + Closes #11675 -Stefan Eissing (25 Apr 2023) +- tests: TLS session sharing test -- Websocket en-/decoding + - test TLS session sharing with special test client + - expect failure with wolfSSL + - disable flaky wolfSSL test_02_07b - - state is fully kept at connection, since curl_ws_send() and - curl_ws_rec() have lifetime beyond usual transfers - - no more limit on frame sizes + Closes #11675 - Reported-by: simplerobot on github - Fixes #10962 - Closes #10999 +Daniel Stenberg (17 Aug 2023) -Patrick Monnerat (25 Apr 2023) +- CURLOPT_*TIMEOUT*: extend and clarify -- urldata: copy CURLOPT_AWS_SIGV4 value on handle duplication + Closes #11686 - Prior to this change STRING_AWS_SIGV4 (CURLOPT_AWS_SIGV4) was wrongly - marked as binary data that could not be duplicated. +- urlapi: return CURLUE_BAD_HOSTNAME if puny2idn encoding fails - Without this fix, this option's value is not copied upon calling - curl_easy_duphandle(). + And document it. Only return out of memory when it actually is a memory + problem. - Closes https://github.com/curl/curl/pull/11021 + Pointed-out-by: Jacob Mealey + Closes #11674 -Stefan Eissing (25 Apr 2023) +Mathew Benson (17 Aug 2023) -- http3: expire unpaused transfers in all HTTP/3 backends +- cmake: add GnuTLS option - Closes #11005 + - Option to use GNUTLS was missing. Hence was not able to use GNUTLS + with ngtcp2 for http3. -- http2: always EXPIRE_RUN_NOW unpaused http/2 transfers + Closes #11685 - - just increasing the http/2 flow window does not necessarily - make a server send new data. It may already have exhausted - the window before +Daniel Stenberg (16 Aug 2023) - Closes #11005 +- RELEASE-NOTES: synced -- http2: pass `stream` to http2_handle_stream_close to avoid NULL checks +- http: remove the p_pragma struct field - Closes #11005 + unused since 40e8b4e52 (2008) -- h2/h3: replace `state.drain` counter with `state.dselect_bits` + Closes #11681 - - `drain` was used by http/2 and http/3 implementations to indicate - that the transfer requires send/recv independant from its socket - poll state. Intended as a counter, it was used as bool flag only. - - a similar mechanism exists on `connectdata->cselect_bits` where - specific protocols can indicate something similar, only for the - whole connection. - - `cselect_bits` are cleard in transfer.c on use and, importantly, - also set when the transfer loop expended its `maxloops` tries. - `drain` was not cleared by transfer and the http2/3 implementations - had to take care of that. - - `dselect_bits` is cleared *and* set by the transfer loop. http2/3 - does no longer clear it, only set when new events happen. +Jay Satiro (16 Aug 2023) - This change unifies the handling of socket poll overrides, extending - `cselect_bits` by a easy handle specific value and a common treatment in - transfers. +- CURLINFO_CERTINFO.3: better explain curl_certinfo struct - Closes #11005 + Closes https://github.com/curl/curl/pull/11666 -Daniel Stenberg (25 Apr 2023) +- CURLINFO_TLS_SSL_PTR.3: clarify a recommendation -- socketpair: verify with a random value + - Remove the out-of-date SSL backend list supported by + CURLOPT_SSL_CTX_FUNCTION. - ... instead of using the curl time struct, since it would use a few - uninitialized bytes and the sanitizers would complain. This is a neater - approach I think. + It makes more sense to just refer to that document instead of having + a separate list that has to be kept in sync. - Reported-by: Boris Kuschel - Fixes #10993 - Closes #11015 + Closes https://github.com/curl/curl/pull/11665 -Stefan Eissing (25 Apr 2023) +- write-out.d: clarify %{time_starttransfer} -- HTTP3: document the ngtcp2/nghttp3 versions to use for building curl + sync it up with CURLINFO_STARTTRANSFER_TIME_T - - refs #11011 to clarify this for people building curl themselves +Daniel Stenberg (15 Aug 2023) - Closes #11019 +- transfer: don't set TIMER_STARTTRANSFER on first send -Daniel Stenberg (25 Apr 2023) + The time stamp is for measuring the first *received* byte -- lib: unify the upload/method handling + Fixes #11669 + Reported-by: JazJas on github + Closes #11670 - By making sure we set state.upload based on the set.method value and not - independently as set.upload, we reduce confusion and mixup risks, both - internally and externally. +trrui-huawei (15 Aug 2023) - Closes #11017 +- quiche: enable quiche to handle timeout events -- RELEASE-NOTES: synced + In parallel with ngtcp2, quiche also offers the `quiche_conn_on_timeout` + interface for the application to invoke upon timer + expiration. Therefore, invoking the `on_timeout` function of the + Connection is crucial to ensure seamless functionality of quiche with + timeout events. -Dan Fandrich (24 Apr 2023) + Closes #11654 -- CI: don't run CI jobs if only another CI was changed +- quiche: adjust quiche `QUIC_IDLE_TIMEOUT` to 60s - A few paths were missed in the last commit, as well as a job added since - then. + Set the `QUIC_IDLE_TIMEOUT` parameter to match ngtcp2 for consistency. - Followup-to 395b9175 +Daniel Stenberg (15 Aug 2023) -- CI: adjust labeler match patterns +- KNOWN_BUGS: LDAPS requests to ActiveDirectory server hang -- runtests: support buffering log messages in runner & servers + Closes #9580 - Log messages generated with logmsg can now be buffered and returned from - the runner as a return value. This will be needed with parallel testing - to allow all messages for one test to be displayed together instead of - interspersed with messages of multiple tests. Buffering can be disabled - by setting a logging callback function with setlogfunc, which is - currently being done to preserve existing logging behaviour for now. +- imap: add a check for failing strdup() - Some additional output is generated in verbose and debugprotocol modes, - which don't always use logmsg. These modes also impact some servers - which generate extra messages. No attempt is made to buffer everything - if these modes are enabled. +- imap: remove the only sscanf() call in the IMAP code - Ref: #10818 - Closes #11016 + Avoids the use of a stack buffer. -- runtests: more consistently use logmsg in server control code + Closes #11673 - Also, display an error when sshversioninfo returns one. +- imap: use a dynbuf in imap_atom - Ref: #10818 + Avoid a calculation + malloc. Build the output in a dynbuf. -- runtests: create runner functions for clearlocks and stopservers + Closes #11672 - runtests.pl now uses runner for all server actions beyond the initial - variable configuration. +Marin Hannache (14 Aug 2023) - Ref: #10818 +- http: do not require a user name when using CURLAUTH_NEGOTIATE -- runtests: tightened servers package exports + In order to get Negotiate (SPNEGO) authentication to work in HTTP you + used to be required to provide a (fake) user name (this concerned both + curl and the lib) because the code wrongly only considered + authentication if there was a user name provided, as in: - The defaults are intended for runtests.pl, whereas runner.pm needs to - explicitly specify them. + curl -u : --negotiate https://example.com/ -- runtests: display logs on server failure in singletest() + This commit leverages the `struct auth` want member to figure out if the + user enabled CURLAUTH_NEGOTIATE, effectively removing the requirement of + setting a user name both in curl and the lib. - This is closer to the place where logs are displayed on test failure. - Also, only display these logs if -p is given, which is the same flag - that controls display of test failure logs. Some server log files - need to be deleted later so that they stay around long enough to be - displayed on failure. + Signed-off-by: Marin Hannache + Reported-by: Enrico Scholz + Fixes https://sourceforge.net/p/curl/bugs/440/ + Fixes #1161 + Closes #9047 - Ref: #10818 +Viktor Szakats (13 Aug 2023) -- runtests: turn a print into a logmsg +- build: streamline non-UWP wincrypt detections - Also enable another couple of useful messages in verbose mode. + - with CMake, use the variable `WINDOWS_STORE` to detect an UWP build + and disable our non-UWP-compatible use the Windows crypto API. This + allows to drop two dynamic feature checks. - Ref: #10818 + `WINDOWS_STORE` is true when invoking CMake with + `CMAKE_SYSTEM_NAME` == `WindowsStore`. Introduced in CMake v3.1. -Daniel Stenberg (24 Apr 2023) + Ref: https://cmake.org/cmake/help/latest/variable/WINDOWS_STORE.html -- http: store the password in the correct variable + - with autotools, drop the separate feature check for `wincrypt.h`. On + one hand this header has been present for long (even Borland C 5.5 had + it from year 2000), on the other we used the check result solely to + enable another check for certain crypto functions. This fails anyway + with the header not present. We save one dynamic feature check at the + configure stage. - Typo from fc2f1e547a4a, detected by Coverity (because there's dead code - due to this). + Reviewed-by: Marcel Raad + Closes #11657 - Closes #11002 +Nicholas Nethercote (13 Aug 2023) -Stefan Eissing (24 Apr 2023) +- docs/HYPER.md: update hyper build instructions -- HTTP3/quiche: terminate h1 response header when no body is sent + Nightly Rust and `-Z unstable-options` are not needed. - - fixes a failure in test2501 where a response without body was missing - the final empty line + The instructions here now match the hyper docs exactly: + https://github.com/hyperium/hyper/commit/bd7928f3dd6a8461f0f0fdf7ee0fd95c2f15 + 6f88 - Closes #11003 + Closes #11662 -Dan Fandrich (22 Apr 2023) +Daniel Stenberg (13 Aug 2023) -- runtests: move showdiff into runtests.pl +- RELEASE-NOTES: synced - It's not used anywhere else. +- urlapi: CURLU_PUNY2IDN - convert from punycode to IDN name -- devtest: add a new script for testing the test harness + Asssisted-by: Jay Satiro + Closes #11655 - This is currently useful for starting a test server on its own without - an associated test, which can be used for interactive curl testing or - for validating parts of the test harness itself. More commands can be - added to perform additional functions in the future. +- spellcheck: adapt to backslashed minuses - Ref: #10818 - Closes #11008 + As the curl.1 has more backslashed minus, the cleanup sed lines xneed to + adapt. -- runtests: refactor the main test loop into two + Adjusted some docs slighly. - The test loop now has an initial loop that first runs through all - possible tests to build a set of those to attempt on this run based on - features and keywords and only then goes through that new list to run - them. This actually makes it three loops through all tests cases, as - there is an existing loop that gathers possible test numbers from the - test files on disk. + Follow-up to 439ff2052e - This has two minor effects on the output: all the tests that will be - skipped are displayed at the start (instead of being interspersed with - other tests) and the -l option no longer shows a count of tests at the - end or a (misleading) statement that tests have run successfully. The - skipped tests are also omitted from the test results sent to AppVeyor - and Azure in CI builds. + Closes #11663 - Another effect is a reduction in the amount of work considered part of - the "Test definition reading and preparation time" reported with -r - making those figures slightly lower than before. +- gen: escape more minus - Ref: #10818 + Detected since it was still hard to search for option names using dashes + in the middle in the man page. -- runtests: track only the current test timings in runner.pm + Closes #11660 - This avoids passing these data through through global variables, which - soon won't be possible. +- cookie-jar.d: enphasize that this option is ONLY writing cookies - Ref: #10818 + Reported-by: Dan Jacobson + Tweaked-by: Jay Satiro + Ref: #11642 + Closes #11661 -- runtests: skip test preprocessing when doing -l +Nicholas Nethercote (11 Aug 2023) - This speeds up the output tremendously by avoiding unnecessary work. +- docs/HYPER.md: document a workaround for a link error -- runtests: simplify value returned regarding use of valgrind + Closes #11653 - As a side effect this will now also show in verbose mode that valgrind - is being skipped on tests that explicitly disable it, such as 600. +Jay Satiro (11 Aug 2023) - Ref: #10818 +- schannel: verify hostname independent of verify cert -- runtests: fix quoting in Appveyor and Azure test integration + Prior to this change when CURLOPT_SSL_VERIFYPEER (verifypeer) was off + and CURLOPT_SSL_VERIFYHOST (verifyhost) was on we did not verify the + hostname in schannel code. - Test 1442's name was not quoted correctly so wasn't registered in - Appveyor and it had the wrong name in Azure. The JSON string quotes were - also invalid, even though both servers happened to accept it regardless. + This fixes KNOWN_BUG 2.8 "Schannel disable CURLOPT_SSL_VERIFYPEER and + verify hostname". We discussed a fix several years ago in #3285 but it + went stale. - Closes #11010 + Assisted-by: Daniel Stenberg -Daniel Stenberg (19 Apr 2023) + Bug: https://curl.haxx.se/mail/lib-2018-10/0113.html + Reported-by: Martin Galvan -- RELEASE-NOTES: synced + Ref: https://github.com/curl/curl/pull/3285 -Dan Fandrich (18 Apr 2023) + Fixes https://github.com/curl/curl/issues/3284 + Closes https://github.com/curl/curl/pull/10056 -- runtests: spread out the port numbers used by servers +Daniel Stenberg (11 Aug 2023) - The server ports are chosen randomly for each server, but the random - ranges chosen were inconsistently-sized and overlapping. Now, they are - spread out more so at least the first random port chosen for each server - is guaranteed to not also be chosen by another server. The starting port - numbers are also raised to put them in the Ephemeral Port range—not the - range defined by RFC 6335 but the one used by Linux, which starts lower - and gives us more room to work with. +- curl_quiche: remove superfluous NULL check - Reported-by: Daniel Stenberg + 'stream' is always non-NULL at this point -- runtests: fix problems on failure + Pointed out by Coverity - The verify time must be set in this case, like all cases. An error - message needs to be displayed as well. + Closes #11656 -- runtests: fix perl warning when is wrong +- curl/urlapi.h: tiny typo -- runtests: don't try to stop stunnel before trying again +- github/labeler: make HYPER.md set Hyper and not TLS - Calling stopserver() before retrying stunnel due to an error would stop - the dependent server (such as HTTP) meaning stunnel would have nothing - to talk to when it came up. Don't try to force a stop when it didn't - actually start. Also, don't mark the server as bad for future use when - it starts up on a retry. +- docs/cmdline-opts/gen.pl: hide "added in" before 7.50.0 - Reported-by: eaglegai at github - Tested-by: eaglegai at github - Fixes #10976 + 7.50.0 shipped on Jul 21 2016, over seven years ago. We no longer need + to specify version changes for earlier releases in the generated output. -- runtests: don't accidentally randomly choose the same port + This ups the limit from the previous 7.30.0 (Apr 12 2013) - If a server couldn't be started on a port, a new one is randomly chosen - and the server is tried again. Avoid accidentally using a - randomly-chosen 0 port offset by adding 1 to the random number. + This hides roughly 35 "added in" mentions. - Found-by: Daniel Stenberg + Closes #11651 -- runtests: don't attempt to use a port we know is in use +Jay Satiro (10 Aug 2023) - This reduces the startup time when there is a known conflict on the - random port chosen for a server. This was already done for stunnel, but - now it's done for all servers. +- bug_report: require reporters to specify curl and os versions -- http-server: fix server name in a log message + - Change curl version and os sections from single-line input to + multi-line textarea. - This changed when the file was renamed in commit cbf57176 + - Require curl version and os sections to be filled out before report + can be submitted. -- runtests: refactor into more packages + Closes https://github.com/curl/curl/pull/11636 - testutil.pm now contains a few miscellaneous functions that are used in - several places but have no better place to live. subvariables moves to - servers.pm since most variables that it substitutes relate to servers, - so this is the most appropriate place. Rename a few functions for better - naming consistency. +Daniel Stenberg (9 Aug 2023) - Ref: #10818 - Closes #10995 +- gen.pl: replace all single quotes with aq -- runtests: call timestampskippedevents() in singletest + - this prevents man from using a unicode sequence for them + - which then allows search to work properly - ..rather than by the runner + Closes #11645 -- runtests: assume a newer Valgrind by default +Viktor Szakats (9 Aug 2023) - The tests for an older Valgrind version should probably just be deleted, - given that they're testing for an 18-year-old version. +- cmake: fix to use variable for the curl namespace -- runtests: refactor test runner code into runner.pm + Replace (wrong) literal with a variable to specify the curl + namespace. - This is code that is directly responsible for running a single test. - This will eventually run in a separate process as part of the parallel - testing project. + Follow-up to 1199308dbc902c52be67fc805c72dd2582520d30 #11505 - Ref: #10818 + Reported-by: balikalina on Github + Fixes https://github.com/curl/curl/commit/1199308dbc902c52be67fc805c72dd25825 + 20d30#r123923098 + Closes #11629 -- runtests: skip unneeded work if test won't be running +- cmake: allow `SHARE_LIB_OBJECT=ON` on all platforms - This speeds up tests by avoiding unnecessary processing. + 2ebc74c36a19a1700af394c16855ce144d9878e3 #11546 introduced sharing + libcurl objects for shared and static targets. - Ref: #10818 + The above automatically enabled for Windows builds, with an option to + disable with `SHARE_LIB_OBJECT=OFF`. -- runtests: factor out singletest_postcheck + This patch extend this feature to all platforms as a manual option. + You can enable it by setting `SHARE_LIB_OBJECT=ON`. Then shared objects + are built in PIC mode, meaning the static lib will also have PIC code. - This will eventually need to be part of the test runner. + [EXPERIMENTAL] - Ref: #10818 + Closes #11627 -- test303: kill server after test +- cmake: assume `wldap32` availability on Windows - Otherwise, an HTTP test closely following this one with a tight time - constraint (e.g. 672) could fail because the test server stays sitting - with the wait command for a while. + This system library first shipped with Windows ME, available as an extra + install for some older releases (according to [1]). The import library + was present already in old MinGW 3.4.2 (year 2007). -Patrick Monnerat (18 Apr 2023) + Drop the feature check and its associated `HAVE_WLDAP32` variable. -- OS400: provide ILE/RPG usage examples + To manually disable `wldap32`, you can use the `USE_WIN32_LDAP=OFF` + CMake option, like before. - Closes https://github.com/curl/curl/pull/10994 + [1]: https://dlcdn.apache.org/httpd/binaries/win32/LEGACY.html -- OS400: improve vararg emulation + Reviewed-by: Jay Satiro + Closes #11624 - - Use V7R4 RPG procedure overloading to improve vararg emulation. +Daniel Stenberg (9 Aug 2023) - From OS400 V7R4 and above, ILE/RPG implements a limited procedure - overloading feature that can be used to improve curl's typed - implementation of varargs procedures. This commit applies it to - curl_easy_setopt(), curl_multi_setopt(), curl_share_setopt() and - curl_easy_getinfo(). +- page-header: move up a URL paragraph from GLOBBING to URL - Closes https://github.com/curl/curl/pull/10994 +- variable.d: output the function names table style -- OS400: fix and complete ILE/RPG binding + Also correct the url function name in the header - - Fix wrong definitions of CURL_ZERO_TERNINATED, curl_mime_data() and - curl_mime_data_ccsid(). + Closes #11641 - - Add recent definitions, in particular blob, header API and WebSockets - API. +- haproxy-clientip.d: remove backticks - - Support for CURLVERSION_ELEVENTH. + This is not markdown - - New functions for EBCDIC support. + Follow-up to 0a75964d0d94a4 - Reflect these changes in README.OS400. + Closes #11639 - Closes https://github.com/curl/curl/pull/10994 +- RELEASE-NOTES: synced -- OS400: implement EBCDIC support for recent features +- gen.pl: escape all dashes (ascii minus) to avoid unicode hyphens - - Support CURLVERSION_ELEVENTH. + Reported-by: FC Stegerman + Fixes #11635 + Closes #11637 - - New function curl_url_strerror_ccsid(). +- cmdline-opts/page-header: reorder, clean up - - curl_easy_setopt_ccsid() supports blobs and 3 recent string options. + - removed some unnecessary blurb to focus + - moved up the more important URL details + - put "globbing" into its own subtitle and moved down a little + - mention the online man page in the version section - - New function curl_easy_header_ccsid(). + Closes #11638 - - New generic latin1<-->ccsid conversion functions curl_from_ccsid() and - curl_to_ccsid() for user convenience. +- c-hyper: adjust the hyper to curlcode conversion - - README.OS400 updated accordingly. + Closes #11621 - - Removed a leftover QsoSSL support identifier. +- test2306: make it use a persistent connection - Closes https://github.com/curl/curl/pull/10994 + + enable verbose already from the start -- OS400: rework build scripts + Closes #11621 - - Rename shell function "system" to "CLcommand" to avoid confusion with - built-in command. +eppesuig (8 Aug 2023) - - Reformat scripts. Fix some indentations. Avoid lines > 80 characters - where possible. +- list-only.d: mention SFTP as supported protocol - - Support ASCII runtime development files in a user-defined directory - path. + Closes #11628 - - FIX SONAME detection. +Daniel Stenberg (8 Aug 2023) - - Drop form API test program compilation (does not exist anymore). +- request.d: use .TP for protocol "labels" - Closes https://github.com/curl/curl/pull/10994 + To render the section nicer in man page. -Sevan Janiyan (18 Apr 2023) + Closes #11630 -- tests/sshserver.pl: Define AddressFamily earlier +- cf-haproxy: make CURLOPT_HAPROXY_CLIENT_IP set the *source* IP - As the comment states "Address family must be specified before ListenAddress" - , otherwise the tests fail to run - `"failed starting SSH server" 52 times (582, 583, 600, 601, 602, 603, 604, 60 - 5, 606 and 43 more)` + ... as documented. - Closes #10983 + Update test 3201 and 3202 accordingly. -Stefan Eissing (18 Apr 2023) + Reported-by: Markus Sommer + Fixes #11619 + Closes #11626 -- quiche: Enable IDLE egress handling +- page-footer: QLOGDIR works with ngtcp2 and quiche - Follow-up to 544abeea which added the handling but wrongly left it - commented out. + It previously said "both" backends which is confusing as we currently + have three... - Closes https://github.com/curl/curl/pull/11000 + Closes #11631 -Daniel Stenberg (18 Apr 2023) +Stefan Eissing (8 Aug 2023) -- docs/examples/protofeats.c: Outputs all protocols and features +- http3: quiche, handshake optimization, trace cleanup - Showing off one way to get to char pointer arrays of info returned by - curl_version_info() + - load x509 store after clienthello + - cleanup of tracing - Closes #10991 + Closes #11618 -- tests/keywords.pl: remove +Daniel Stenberg (8 Aug 2023) - This script does not work since the introduction of the test - preprocessing. If we need this functionality, it probably needs to be - moved into the runtests tool or similar. +- ngtcp2: remove dead code - Reported-by: Dan Fandrich - Fixes #10895 - Closes #10987 + 'result' is always zero (CURLE_OK) at this point -Stefan Eissing (17 Apr 2023) + Detected by Coverity -- http2: support HTTP/2 to forward proxies, non-tunneling + Closes #11622 - - with `--proxy-http2` allow h2 ALPN negotiation to - forward proxies - - applies to http: requests against a https: proxy only, - as https: requests will auto-tunnel - - adding a HTTP/1 request parser in http1.c - - removed h2h3.c - - using new request parser in nghttp2 and all h3 backends - - adding test 2603 for request parser - - adding h2 proxy test cases to test_10_* +Viktor Szakats (8 Aug 2023) - scorecard.py: request scoring accidentally always run curl - with '-v'. Removed that, expect double numbers. +- openssl: auto-detect `SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED` - labeller: added http1.* and h2-proxy sources to detection + OpenSSL 1.1.1 defines this macro, but no ealier version, or any of the + popular forks (yet). Use the macro itself to detect its presence, + replacing the hard-wired fork-specific conditions. - Closes #10967 + This way the feature will enable automatically when forks implement it, + while also shorter and possibly requiring less future maintenance. -Daniel Stenberg (17 Apr 2023) + Follow-up to 94241a9e78397a2aaf89a213e6ada61e7de7ee02 #6721 -- curl_easy_unescape.3: rename the argument + Reviewed-by: Jay Satiro + Closes #11617 - and highlight it appropriately in the text. +- openssl: use `SSL_CTX_set_ciphersuites` with LibreSSL 3.4.1 - Closes #10979 + LibreSSL 3.4.1 (2021-10-14) added support for + `SSL_CTX_set_ciphersuites`. -Viktor Szakats (17 Apr 2023) + Ref: https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.4.1-relnotes.txt -- autotools: sync up clang picky warnings with cmake + Reviewed-by: Jay Satiro + Closes #11616 - Bringing missing options over from CMake. +- openssl: use `SSL_CTX_set_keylog_callback` with LibreSSL 3.5.0 - Move around existing `-Wno-pointer-bool-conversion` option to come - _after_ `-Wconversion`. + LibreSSL 3.5.0 (2022-02-24) added support for + `SSL_CTX_set_keylog_callback`. - Reviewed-by: Marcel Raad - Closes #10974 + Ref: https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.5.0-relnotes.txt -Daniel Stenberg (17 Apr 2023) + Reviewed-by: Jay Satiro + Closes #11615 -- tests/libtest/lib1900.c: remove +- cmake: drop `HAVE_LIBWINMM` and `HAVE_LIBWS2_32` feature checks - This file was left behind when the rest of the test was previously removed. + - `HAVE_LIBWINMM` was detected but unused. The `winmm` system library is + also not used by curl, but it is by its optional dependency `librtmp`. + Change the logic to always add `winmm` when `USE_LIBRTMP` is set. This + library has been available since the early days of Windows. - Follow-up to e50a877df74f + - `HAVE_LIBWS2_32` detected `ws2_32` lib on Windows. This lib is present + since Windows 95 OSR2 (AFAIR). Winsock1 already wasn't supported and + other existing logic already assumed this lib being present, so delete + the check and replace the detection variable with `WIN32` and always + add `ws2_32` on Windows. -- src/tool_operhlp.c: fix value stored to 'uerr' is never read + Closes #11612 - Ref: https://github.com/curl/curl/pull/10974#issuecomment-1510461343 - Reported-by: Viktor Szakats - Closes #10982 +Daniel Gustafsson (8 Aug 2023) -Viktor Szakats (16 Apr 2023) +- crypto: ensure crypto initialization works -- cmake: speed up and extend picky clang/gcc options + Make sure that context initialization during hash setup works to avoid + going forward with the risk of a null pointer dereference. - Extend existing picky compiler options with ones missing compared to - autotools builds. Also sync options between clang and gcc. + Reported-by: Philippe Antoine on HackerOne + Assisted-by: Jay Satiro + Assisted-by: Daniel Stenberg - Redesign the way we enable these options to avoid the slow option - detection almost completely. + Closes #11614 - This reduces the number of detections from 35 to zero for clang and - 3 for gcc, even after adding a bunch of new options. +Viktor Szakats (7 Aug 2023) - clang 3.0 (2011-11-29) and gcc 2.95 (1999-07-31) now required. +- openssl: switch to modern init for LibreSSL 2.7.0+ - Also show enabled picky options. + LibreSSL 2.7.0 (2018-03-21) introduced automatic initialization, + `OPENSSL_init_ssl()` function and deprecated the old, manual init + method, as seen in OpenSSL 1.1.0. Switch to the modern method when + available. - Ref: https://github.com/libssh2/libssh2/pull/952 + Ref: https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.7.0-relnotes.txt Reviewed-by: Daniel Stenberg - Closes #10973 + Closes #11611 -Andreas Falkenhahn (16 Apr 2023) +Daniel Stenberg (7 Aug 2023) -- nbtlm: use semicolons instead of commas for (void) args +- gskit: remove - Closes #10978 + We remove support for building curl with gskit. -Daniel Stenberg (15 Apr 2023) + - This is a niche TLS library, only running on some IBM systems + - no regular curl contributors use this backend + - no CI builds use or verify this backend + - gskit, or the curl adaption for it, lacks many modern TLS features + making it an inferior solution + - build breakages in this code take weeks or more to get detected + - fixing gskit code is mostly done "flying blind" -- multi: free up more data earleier in DONE + This removal has been advertized in DEPRECATED in Jan 2, 2023 and it has + been mentioned on the curl-library mailing list. - Before checking for more users of the connection and possibly bailing - out. + It could be brought back, this is not a ban. Given proper effort and + will, gskit support is welcome back into the curl TLS backend family. - Fixes #10971 - Reported-by: Paweł Wegner - Closes #10972 + Closes #11460 - RELEASE-NOTES: synced -- curl: do NOT append file name to path for upload when there's a query +Dan Fandrich (7 Aug 2023) - Added test 425 to verify. +- THANKS-filter: add a name typo - Reported-by: Dirk Rosenkranz - Bug: https://curl.se/mail/archive-2023-04/0008.html - Closes #10969 +Stefan Eissing (7 Aug 2023) -- libcurl-thread.3: improved name resolver wording +- http3/ngtcp2: shorten handshake, trace cleanup - And make better .SH sections + - shorten handshake timing by delayed x509 store load (OpenSSL) + as we do for HTTP/2 + - cleanup of trace output, align with HTTP/2 output - Closes #10966 + Closes #11609 -Colman Mbuya (14 Apr 2023) +Daniel Stenberg (7 Aug 2023) -- CURLOPT_PROXY_SSL_VERIFYPEER.3: fix minor grammar mistake +- headers: accept leading whitespaces on first response header - Closes #10968 + This is a bad header fold but since the popular browsers accept this + violation, so does curl now. Unless built with hyper. -Daniel Stenberg (14 Apr 2023) + Add test 1473 to verify and adjust test 2306. -- curl: add --proxy-http2 + Reported-by: junsik on github + Fixes #11605 + Closes #11607 - For trying HTTP/2 with an HTTPS proxy. +- include/curl/mprintf.h: add __attribute__ for the prototypes - Closes #10926 + - if gcc or clang is used + - if __STDC_VERSION__ >= 199901L, which means greater than C90 + - if not using mingw + - if CURL_NO_FMT_CHECKS is not defined -- KNOWN_BUGS: remove fixed or outdated issues, move non-bugs + Closes #11589 - - remove h3 issues believed to be fixed +- tests: fix bad printf format flags in test code - - make the flaky CI issue be generic and not Windows specific +- tests: fix header scan tools for attribute edits in mprintf.h - - "TLS session cache does not work with TFO" now documented +- cf-socket: log successful interface bind - This is now a documented restriction and not a bug. TFO in general is - rarely used and has other problems, making it a low-priotity thing to - work on. + When the setsockopt SO_BINDTODEVICE operation succeeds, output that in + the verbose output. - - remove "Renegotiate from server may cause hang for OpenSSL backend" + Ref: #11599 + Closes #11608 - This is an OpenSSL issue, not a curl one. Even if it taints curl. +- CURLOPT_SSL_VERIFYPEER.3: mention it does not load CA certs when disabled - - rm "make distclean loops forever" + Ref: #11457 + Closes #11606 - - rm "configure finding libs in wrong directory" +- CURLOPT_SSL_VERIFYPEER.3: add two more see also options - Added a section to docs/INSTALL.md about it. + CURLINFO_CAINFO and CURLINFO_CAPATH - - "A shared connection cache is not thread-safe" + Closes #11603 - Moved over to TODO and expanded for other sharing improvements we - could do +- KNOWN_BUGS: aws-sigv4 does not behave well with AWS VPC Lattice - - rm "CURLOPT_OPENSOCKETPAIRFUNCTION is missing" + Closes #11007 - - rm "Blocking socket operations in non-blocking API" +Graham Campbell (6 Aug 2023) - Already listed as a TODO +- CI: use openssl 3.0.10+quic, nghttp3 0.14.0, ngtcp2 0.18.0 - - rm "curl compiled on OSX 10.13 failed to run on OSX 10.10" + Closes #11585 - Water under the bridge. No one cares about this anymore. +Daniel Stenberg (6 Aug 2023) - - rm "build on Linux links libcurl to libdl" +- TODO: add *5* entries for aws-sigv4 - Verified to not be true (anymore). + Closes #7559 + Closes #8107 + Closes #8810 + Closes #9717 + Closes #10129 - - rm "libpsl is not supported" +- TODO: LDAP Certificate-Based Authentication - The cmake build supports it since cafb356e19cda22 + Closes #9641 - Closes #10963 +Stefan Eissing (6 Aug 2023) -- url: fix PVS nits +- http2: cleanup trace messages - - expression 'hostptr' is always true - - a part of conditional expression is always true: proxypasswd - - expression 'proxyuser' is always true - - avoid multiple Curl_now() calls in allocate_conn + - more compact format with bracketed stream id + - all frames traced in and out - Ref: #10929 - Closes #10959 + Closes #11592 -- bufq: simplify since expression is always true +Daniel Stenberg (6 Aug 2023) - The check for 'len' is already done so it will remain true until - updated. Pointed out by PVS. +- tests/tftpd+mqttd: make variables static to silence picky warnings - Ref: #10929 - Closes #10958 + Closes #11594 -- hash: fix assigning same value +- docs/cmdline: remove repeated working for negotiate + ntlm - Pointed out by PVS + The extra wording is added automatically by the gen.pl tool - Ref: #10929 - Closes #10956 + Closes #11597 -- cookie: address PVS nits +- docs/cmdline: add small "warning" to verbose options - - avoid assigning the same value again - - remove superfluous check of co->domain - - reduce variable scope for namep/valuep + "Note that verbose output of curl activities and network traffic might + contain sensitive data, including user names, credentials or secret data + content. Be aware and be careful when sharing trace logs with others." - Ref: #10929 - Closes #10954 + Closes #11596 -Stefan Eissing (14 Apr 2023) +- RELEASE-NOTES: synced -- cf-socket: Disable socket receive buffer by default +- pingpong: don't use *bump_headersize - - Disable socket receive buffer unless USE_RECV_BEFORE_SEND_WORKAROUND - is in place. + We use that for HTTP(S) only. - While we would like to use the receive buffer, we have stalls in - parallel transfers where not all buffered data is consumed and no socket - events happen. + Follow-up to 3ee79c1674fd6 - Note USE_RECV_BEFORE_SEND_WORKAROUND is a Windows sockets workaround - that has been disabled by default since b4b6e4f1, due to other bugs. + Closes #11590 - Closes https://github.com/curl/curl/pull/10961 +- urldata: remove spurious parenthesis to unbreak no-proxy build -- cf-h2-proxy: fix processing ingress to stop too early + Follow-up to e12b39e13382 - - progress ingress stopped too early, causing data - from the underlying filters to not be processed and - report that no tunnel data was available - - this lead to "hangers" where no socket activity was - seen but data rested in buffers + Closes #11591 - Closes #10952 +- easy: don't call Curl_trc_opt() in disabled-verbose builds -- http3: check stream_ctx more thoroughly in all backends + Follow-up to e12b39e133822c6a0 - - callbacks and filter methods might be invoked at unexpected - times, e.g. when the transfer's stream_ctx has not been initialized - yet or, more likely, has already been taken down. - - check for existance of stream_ctx in such places and return - an error or silently succeed the call. + Closes #11588 - Closes #10951 +- http: use %u for printfing int -Daniel Stenberg (13 Apr 2023) + Follow-up to 3ee79c1674fd6f99e8efca5 -- ftp: fix 'portsock' variable was assigned the same value + Closes #11587 - Pointed out by PVS +Goro FUJI (3 Aug 2023) - Ref: #10929 - Closes #10955 +- vquic: show stringified messages for errno -- ftp: remove dead code + Closes #11584 - This condition can never be true here since it is handled already 28 - lines above. +Stefan Eissing (3 Aug 2023) - Pointed out by PVS. +- trace: make tracing available in non-debug builds - Ref: #10929 - Closes #10957 + Add --trace-config to curl -- cf-h1-proxy: skip an extra NULL assign + Add curl_global_trace() to libcurl - and use Curl_safefree() once to save another NULL assign. Found by PVS. + Closes #11421 - Ref. #10929 - Closes #10953 +Daniel Stenberg (3 Aug 2023) -Philip Heiduck (13 Apr 2023) +- TODO: remove "Support intermediate & root pinning for PINNEDPUBLICKEY" -- GHA: suppress git clone output + See also https://github.com/curl/curl/pull/7507 - Follow-up: https://github.com/curl/curl/commit/8203aa6ed405ec832d2c62f18dfda2 - 93f89a23f9 +- TODO: add "WebSocket read callback" - Closes #10949 + remove "Upgrade to websockets" as we already have this -Stefan Eissing (13 Apr 2023) + Closes #11402 -- cf-socket: remove dead code discovered by PVS +- test497: verify rejecting too large incoming headers - Closes #10960 +- http: return error when receiving too large header set -Daniel Stenberg (13 Apr 2023) + To avoid abuse. The limit is set to 300 KB for the accumulated size of + all received HTTP headers for a single response. Incomplete research + suggests that Chrome uses a 256-300 KB limit, while Firefox allows up to + 1MB. -- http: skip a double NULL assign + Closes #11582 - and also use a local variable to shorten the long names and increase - readability in the function. Pointed out by PVS. +Stefan Eissing (3 Aug 2023) - Ref: #10929 - Closes #10950 +- http2: upgrade tests and add fix for non-existing stream -- mime: skip NULL assigns after Curl_safefree() + - check in h2 filter recv that stream actually exists + and return error if not + - add test for parallel, extreme h2 upgrades that fail if + connections get reused before fully switched + - add h2 upgrade upload test just for completeness - Pointed out by PVS. + Closes #11563 - Ref: #10929 - Closes #10947 +Viktor Szakats (3 Aug 2023) -- rtsp: skip NULL assigns after Curl_safefree() +- tests: ensure `libcurl.def` contains all exports - ... since this is a macro that assigns NULL itself. Pointed out by PVS. + Add `test1279` to verify that `libcurl.def` lists all exported API + functions found in libcurl headers. - Ref: #10929 - Closes #10946 + Also: -- smb: remove double assign + - extend test suite XML `stdout` tag with the `loadfile` attribute. - The same value is assigned the same value already a few lines above. - Pointed out by PVS. + - fix `tests/extern-scan.pl` and `test1135` to include websocket API. - Ref: #10929 - Closes #10945 + - use all headers (sorted) in `test1135` instead of a manual list. -- transfer: skip extra assign + - add options `--sort`, `--heading=` to `tests/extern-scan.pl`. - The 'result' variable already contains CURLE_OK at this point, no use in - setting it again. Pointed out by PVS. + - add `libcurl.def` to the auto-labeler GHA task. - Ref: #10929 - Closes #10944 + Follow-up to 2ebc74c36a19a1700af394c16855ce144d9878e3 -- urlapi: skip a pointless assign + Closes #11570 - It stores a null byte after already having confirmed there is a null - byte there. Detected by PVS. +Daniel Stenberg (2 Aug 2023) - Ref: #10929 - Closes #10943 +- url: change default value for CURLOPT_MAXREDIRS to 30 -Philip Heiduck (13 Apr 2023) + It was previously unlimited by default, but that's not a sensible + default. While changing this has a remote risk of breaking an existing + use case, I figure it is more likely to actually save users from loops. -- GHA: suppress git clone output + Closes #11581 - Closes #10939 +- lib: fix a few *printf() flag mistakes -Stefan Eissing (13 Apr 2023) + Reported-by: Gisle Vanem + Ref: #11574 + Closes #11579 -- tests: make test_12_01 a bit more forgiving on connection counts +Samuel Chiang (2 Aug 2023) -- cf-socket: add socket recv buffering for most tcp cases +- openssl: make aws-lc version support OCSP - - use bufq as recv buffer, also for Windows pre-receive handling - - catch small reads followed by larger ones in a single socket - call. A common pattern on TLS connections. + And bump version in CI - Closes #10787 + Closes #11568 -Daniel Stenberg (13 Apr 2023) +Daniel Stenberg (2 Aug 2023) -- urlapi: cleanups +- tool: make the length argument an int for printf()-.* flags - - move host checks together - - simplify the scheme parser loop and the end of host name parser - - avoid itermediate buffer storing in multiple places - - reduce scope for several variables - - skip the Curl_dyn_tail() call for speed - - detect IPv6 earlier and skip extra checks for such hosts - - normalize directly in dynbuf instead of itermediate buffer - - split out the IPv6 parser into its own funciton - - call the IPv6 parser directly for ipv6 addresses - - remove (unused) special treatment of % in host names - - junkscan() once in the beginning instead of scattered - - make junkscan return error code - - remove unused query management from dedotdotify() - - make Curl_parse_login_details use memchr - - more use of memchr() instead of strchr() and less strlen() calls - - make junkscan check and return the URL length + Closes #11578 - An optimized build runs one of my benchmark URL parsing programs ~41% - faster using this branch. (compared against the shipped 7.88.1 library - in Debian) +- tool_operate: fix memory leak when SSL_CERT_DIR is used - Closes #10935 + Detected by Coverity -Josh McCullough (13 Apr 2023) + Follow-up to 29bce9857a12b6cfa726a5 -- http2: fix typo in infof() call + Closes #11577 - Closes #10940 +- tool/var: free memory on OOM -Daniel Stenberg (12 Apr 2023) + Coverity detected this memory leak in OOM situation -- noproxy: pointer to local array 'hostip' is stored outside scope + Follow-up to 2e160c9c652504e - Ref: #10929 - Closes #10933 + Closes #11575 -Stefan Eissing (12 Apr 2023) +Viktor Szakats (2 Aug 2023) -- connect: fix https connection setup to treat ssl_mode correctly +- gha: bump libressl and mbedtls versions - - for HTTPS protocol, a disabled ssl should never be acceptables. + Closes #11573 - Closes #10934 +Jay Satiro (2 Aug 2023) -Douglas R. Reno (12 Apr 2023) +- schannel: fix user-set legacy algorithms in Windows 10 & 11 -- CMakeLists.txt: fix typo for Haiku detection + - If the user set a legacy algorithm list (CURLOPT_SSL_CIPHER_LIST) then + use the SCHANNEL_CRED legacy structure to pass the list to Schannel. - Closes #10937 + - If the user set both a legacy algorithm list and a TLS 1.3 cipher list + then abort. -Dan Fandrich (11 Apr 2023) + Although MS doesn't document it, Schannel will not negotiate TLS 1.3 + when SCHANNEL_CRED is used. That means setting a legacy algorithm list + limits the user to earlier versions of TLS. -- pathhelp: use the cached $use_cygpath when available + Prior to this change, since 8beff435 (precedes 7.85.0), libcurl would + ignore legacy algorithms in Windows 10 1809 and later. -- runtests: eliminate unneeded variable + Reported-by: zhihaoy@users.noreply.github.com -- runtests: make the # of server start attempts a constant + Fixes https://github.com/curl/curl/pull/10741 + Closes https://github.com/curl/curl/pull/10746 -- runtests: on startup failure call displaylogs only in serverfortest +Daniel Stenberg (2 Aug 2023) - This reduces the number of calls spread throughout the code. +- variable.d: setting a variable again overwrites it - Ref: #10818 - Closes #10919 + Reported-by: Niall McGee + Bug: https://twitter.com/niallmcgee/status/1686523075423322113 + Closes #11571 -- runtests: return an error code with startservers() +Jay Satiro (2 Aug 2023) - The code indicates the kind of failure encountered in starting a server, - which can be used by the caller to tailor the user experience. +- CURLOPT_PROXY_SSL_OPTIONS.3: sync formatting - Ref: #10818 + - Re-wrap CURLSSLOPT_ALLOW_BEAST description. -- runtests: abort early if runpingpongserver is given a bad server type +Daniel Stenberg (2 Aug 2023) -- runtests: don't use the SMB server verification time as reference +- RELEASE-NOTES: synced - %FTPTIME2 and %FTPTIME3 should be set by the FTP server only, for - consistency. +- resolve: use PF_INET6 family lookups when CURL_IPRESOLVE_V6 is set -- tests: factor out the test server management code + Previously it would always do PF_UNSPEC if CURL_IPRESOLVE_V4 is not + used, thus unnecessarily asking for addresses that will not be used. - This now lives in servers.pm with some configuration variables moved to - globalconfig.pm + Reported-by: Joseph Tharayil + Fixes #11564 + Closes #11565 - Ref: #10818 +- docs: link to the website versions instead of markdowns -- runtests: remove an inappropriate use of runclientoutput + ... to make the links work when the markdown is converted to webpages on + https://curl.se - This function is intended for running client code, not servers. + Reported-by: Maurício Meneghini Fauth + Fixes https://github.com/curl/curl-www/issues/272 + Closes #11569 -- runtests: only add $LIBDIR to the path for checktestcmd +Viktor Szakats (1 Aug 2023) - Since checkcmd is for finding servers, there will never be anything in - this directory of interest to them. +- cmake: cache more config and delete unused ones - Ref: #10818 + - cache more Windows config results for faster initialization. -- tests: log sshserver.pl messages to a file + - delete unused config macros `HAVE_SYS_UTSNAME_H`, `HAVE_SSL_H`. - The logmsg messages were thrown away before, so they are now available - for debugging. + - delete dead references to `sys/utsname.h`. -- runtests: also show DISABLED tests with -l + Closes #11551 - Other reasons for skipping tests are ignored for -l, so being explicitly - disabled should be too. +- egd: delete feature detection and related source code -- runtests: move the UNIX sockets into $PIDDIR + EGD is Entropy Gathering Daemon, a socket-based entropy source supported + by pre-OpenSSL v1.1 versions and now deprecated. curl also deprecated it + a while ago. - These were missed when the other server files were moved there. + Its detection in CMake was broken all along because OpenSSL libs were + not linked at the point of feature check. - Follow-up to 70d2fca2 + Delete detection from both cmake and autotools, along with the related + source snippet, and the `--with-egd-socket=` `./configure` option. - Ref: #10818 + Closes #11556 -- tests: tighten up perl exports +Stefan Eissing (1 Aug 2023) - This reduces namespace pollution a little. +- tests: fix h3 server check and parallel instances - Ref: #10818 + - fix check for availability of nghttpx server + - add `tcp` frontend config for same port as quic, as + without this, port 3000 is bound which clashes for parallel + testing -- tests: turn perl modules into full packages + Closes #11553 - This helps enforce more modularization and encapsulation. Enable and fix - warnings on a few packages. Also, rename ftp.pm to processhelp.pm since - there's really nothing ftp-specific in it. +Daniel Stenberg (1 Aug 2023) - Ref: #10818 +- docs/cmdline-opts: spellfixes, typos and polish -Daniel Stenberg (11 Apr 2023) + To make them accepted by the spell checker -- multi: remove a few superfluous assigns + Closes #11562 - PVS found these "The 'rc' variable was assigned the same value." cases. +- CI/spellcheck: build curl.1 and spellcheck it - Ref: #10929 - Closes #10932 + Added acceptable words -- schannel: add clarifying comment + Closes #11562 - Explaining how the PVS warning in #10929 is wrong: Dereferencing of the - null pointer 'backend->cred' might take place. +Alexander Jaeger (1 Aug 2023) - Closes #10931 +- misc: fix various typos -- cookie: clarify that init with data set to NULL reads no file + Closes #11561 - ... and make Curl_cookie_add() require 'data' being set proper with an - assert. +Daniel Stenberg (1 Aug 2023) - The function has not worked with a NULL data for quite some time so this - just corrects the code and comment. +- http2: avoid too early connection re-use/multiplexing - This is a different take than the proposed fixed in #10927 + HTTP/1 connections that are upgraded to HTTP/2 should not be picked up + for reuse and multiplexing by other handles until the 101 switching + process is completed. - Reported-by: Kvarec Lezki - Ref: #10929 - Closes #10930 + Lots-of-debgging-by: Stefan Eissing + Reported-by: Richard W.M. Jones + Bug: https://curl.se/mail/lib-2023-07/0045.html + Closes #11557 -Kvarec Lezki (11 Apr 2023) +- Revert "KNOWN_BUGS: build for iOS simulator on macOS 13.2 with Xcode 14" -- vtls: remove int typecast for sizeof() + This reverts commit 2e8a3d7cb73c85a9aa151e263315f8a496dbb9d4. - V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> - memsize. The value being cast: 'sizeof - (buf->data)'. curl\lib\vtls\vtls.c 2025 + It's a user error for supplying incomplete information to the build system. - https://pvs-studio.com/en/docs/warnings/v220/ + Reported-by: Ryan Schmidt + Ref: https://github.com/curl/curl/issues/11215#issuecomment-1658729367 - Closes #10928 +Viktor Szakats (1 Aug 2023) -Stefan Eissing (11 Apr 2023) +- cmake: add support for single libcurl compilation pass -- http2: fix copynpaste error reported by coverity + Before this patch CMake builds used two separate compilation passes to + build the shared and static libcurl respectively. This patch allows to + reduce that to a single pass if the target platform and build settings + allow it. - - move all code handling HTTP/2 frames for a particular - stream into a separate function to keep from confusing - the call `data` with the stream `data`. + This reduces CMake build times when building both static and shared + libcurl at the same time, making these dual builds an almost zero-cost + option. - Closes #10924 + Enable this feature for Windows builds, where the difference between the + two passes was the use of `__declspec(dllexport)` attribute for exported + API functions for the shared builds. This patch replaces this method + with the use of `libcurl.def` at DLL link time. -Dan Fandrich (11 Apr 2023) + Also update `Makefile.mk` to use `libcurl.def` to export libcurl API + symbols on Windows. This simplifies (or fixes) this build method (e.g. + in curl-for-win, which generated a `libcurl.def` from `.h` files using + an elaborate set of transformations). -- tests: log a too-long Unix socket path in sws and socksd + `libcurl.def` has the maintenance cost of keeping the list of public + libcurl API symbols up-to-date. This list seldom changes, so the cost + is low. - Ref: #10919 + Closes #11546 -Daniel Stenberg (11 Apr 2023) +- cmake: detect `SSL_set0_wbio` in OpenSSL -- gen.pl: error on duplicated See-Also fields + Present in OpenSSL 1.1.0 and BoringSSL. + Missing from LibreSSL 3.8.0. - Updated http2.d accordingly. + Follow-up to f39472ea9f4f4e12cfbc0500c4580a8d52ce4a59 - Closes #10925 + While here, also fix `RAND_egd()` detection which was broken, likely all + along. This feature is probably broken with CMake builds and also + requires a sufficiently obsolete OpenSSL version, so this part of the + update was not tested. -- http2: avoid possible null pointer dereference + Closes #11555 - Reported-by: Dan Fandrich - Fixes #10920 - Closes #10923 +- cmake: fixup H2 duplicate symbols for unity builds -- lib1560: verify that more bad host names are rejected + Closes #11550 - when setting the hostname component of a URL +Pablo Busse (1 Aug 2023) - Closes #10922 +- openssl: Support async cert verify callback -- curl_url_set.3: mention that users can set content rather freely + - Update the OpenSSL connect state machine to handle + SSL_ERROR_WANT_RETRY_VERIFY. - ... which then might render bad URLs if you extract a URL later. + This allows libcurl users that are using custom certificate validation + to suspend processing while waiting for external I/O during certificate + validation. - Closes #10921 + Closes https://github.com/curl/curl/pull/11499 -Dan Fandrich (10 Apr 2023) +Jay Satiro (1 Aug 2023) -- CI: retry failed downloads of aws-lc +- tool_cb_wrt: fix invalid unicode for windows console - Don't fail the build in case of a temporary server problem. + - Suppress an incomplete UTF-8 sequence at the end of the buffer. -- test1169: fix so it works properly everywhere + - Attempt to reconstruct incomplete UTF-8 sequence from prior call(s) + in current call. - - Use an absolute path for the -L option since the module isn't in the - perl path - - Create the needed test file in a section; isn't - intended for this - - Fix the test number in the file name, which was wrong + Prior to this change, in Windows console UTF-8 sequences split between + two or more calls to the write callback would cause invalid "replacement + characters" U+FFFD to be printed instead of the actual Unicode + character. This is because in Windows only UTF-16 encoded characters are + printed to the console, therefore we convert the UTF-8 contents to + UTF-16, which cannot be done with partial UTF-8 sequences. - Follow-up to f754990a + Reported-by: Maksim Arhipov - Ref: #10818 - Fixes #10889 - Closes #10917 + Fixes https://github.com/curl/curl/issues/9841 + Closes https://github.com/curl/curl/pull/10890 -- tests: stop using strndup(), which isn't portable +Daniel Stenberg (1 Aug 2023) - It's not available on Solaris 10, for example. Since this is just test - code that doesn't need to use an optimized system version, replace it - with the implementation copied from tool_cb_hdr.c. +- sectransp: prevent CFRelease() of NULL -- runtests: fix an incorrect comment about the ld_preload feature + When SecCertificateCopyCommonName() returns NULL, the common_name + pointer remains set to NULL which apparently when calling CFRelease() on + (sometimes?) crashes. - Follow-up to 1f631864 + Reported-by: Guillaume Algis + Fixes #9194 + Closes #11554 - Ref: #10818 +Jay Satiro (1 Aug 2023) -Daniel Stenberg (9 Apr 2023) +- vtls: clarify "ALPN: offers" message -- urlapi: prevent setting invalid schemes with *url_set() + Before: + * ALPN: offers h2,http/1.1 - A typical mistake would be to try to set "https://" - including the - separator - this is now rejected as that would then lead to - url_get(... URL...) would get an invalid URL extracted. + After: + * ALPN: curl offers h2,http/1.1 - Extended test 1560 to verify. + Bug: https://curl.se/mail/lib-2023-07/0041.html + Reported-by: Richard W.M. Jones + Closes #11544 - Closes #10911 +Daniel Stenberg (1 Aug 2023) -Biswapriyo Nath (9 Apr 2023) +- urlapi: make sure zoneid is also duplicated in curl_url_dup -- http2: remove unused Curl_http2_strerror function declaration + Add several curl_url_dup() tests to the general lib1560 test. - Curl_http2_strerror was renamed to http2_strerror in - 05b100aee247bb9bec8e9a1b0 and then http2_strerror was removed in - 5808a0d0f5ea0399d4a2a2 + Reported-by: Rutger Broekhoff + Bug: https://curl.se/mail/lib-2023-07/0047.html + Closes #11549 - This also fixes the following compiler error +Sergey (1 Aug 2023) - lib/http2.h:41:33: error: unknown type name 'uint32_t' - lib/http2.h:1:1: note: 'uint32_t' is defined in header '' +- urlapi: fix heap buffer overflow - Closes #10912 + `u->path = Curl_memdup(path, pathlen + 1);` accesses bytes after the null-ter + minator. -Daniel Stenberg (8 Apr 2023) + ``` + ==2676==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x04d48c75 a + t pc 0x0112708a bp 0x006fb7e0 sp 0x006fb3c4 + READ of size 78 at 0x04d48c75 thread T0 + #0 0x1127089 in __asan_wrap_memcpy D:\a\_work\1\s\src\vctools\asan\llvm\c + ompiler-rt\lib\sanitizer_common\sanitizer_common_interceptors.inc:840 + #1 0x1891a0e in Curl_memdup C:\actions-runner\_work\client\client\third_p + arty\curl\lib\strdup.c:97 + #2 0x18db4b0 in parseurl C:\actions-runner\_work\client\client\third_part + y\curl\lib\urlapi.c:1297 + #3 0x18db819 in parseurl_and_replace C:\actions-runner\_work\client\clien + t\third_party\curl\lib\urlapi.c:1342 + #4 0x18d6e39 in curl_url_set C:\actions-runner\_work\client\client\third_ + party\curl\lib\urlapi.c:1790 + #5 0x1877d3e in parseurlandfillconn C:\actions-runner\_work\client\client + \third_party\curl\lib\url.c:1768 + #6 0x1871acf in create_conn C:\actions-runner\_work\client\client\third_p + arty\curl\lib\url.c:3403 + #7 0x186d8dc in Curl_connect C:\actions-runner\_work\client\client\third_ + party\curl\lib\url.c:3888 + #8 0x1856b78 in multi_runsingle C:\actions-runner\_work\client\client\thi + rd_party\curl\lib\multi.c:1982 + #9 0x18531e3 in curl_multi_perform C:\actions-runner\_work\client\client\ + third_party\curl\lib\multi.c:2756 + ``` -- RELEASE-NOTES: synced + Closes #11560 -SuperIlu on github (8 Apr 2023) +Daniel Stenberg (31 Jul 2023) -- config-dos.h: fix SIZEOF_CURL_OFF_T for MS-DOS/DJGPP +- curl: make %output{} in -w specify a file to write to - Fixes #10905 - Closes #10910 + It can be used multiple times. Use %output{>>name} to append. -Daniel Stenberg (8 Apr 2023) + Add docs. Test 990 and 991 verify. -- lib: remove CURLX_NO_MEMORY_CALLBACKS + Idea: #11400 + Suggested-by: ed0d2b2ce19451f2 + Closes #11416 - The only user of this define was 'chkdecimalpoint' - a special purpose - test tool that was built but not used anymore (since 17c18fbc3 - Apr - 2020). +- RELEASE-NOTES: synced - Closes #10908 +- tool: add "variable" support -- CURLPROXY_HTTPS2: for HTTPS proxy that may speak HTTP/2 + Add support for command line variables. Set variables with --variable + name=content or --variable name@file (where "file" can be stdin if set + to a single dash (-)). - Setting this proxy type allows curl to negotiate and use HTTP/2 with - HTTPS proxies. + Variable content is expanded in option parameters using "{{name}}" + (without the quotes) if the option name is prefixed with + "--expand-". This gets the contents of the variable "name" inserted, or + a blank if the name does not exist as a variable. Insert "{{" verbatim + in the string by prefixing it with a backslash, like "\\{{". - Closes #10900 + Import an environment variable with --variable %name. It makes curl exit + with an error if the environment variable is not set. It can also rather + get a default value if the variable does not exist, using =content or + @file like shown above. -Ali Khodkar (8 Apr 2023) + Example: get the USER environment variable into the URL: -- write-out.d: add missing periods + --variable %USER + --expand-url = "https://example.com/api/{{USER}}/method" - Closes #10897 + When expanding variables, curl supports a set of functions that can make + the variable contents more convenient to use. It can trim leading and + trailing white space with "trim", output the contents as a JSON quoted + string with "json", URL encode it with "url" and base 64 encode it with + "b64". To apply functions to a variable expansion, add them colon + separated to the right side of the variable. They are then performed in + a left to right order. -Daniel Stenberg (7 Apr 2023) + Example: get the contents of a file called $HOME/.secret into a variable + called "fix". Make sure that the content is trimmed and percent-encoded + sent as POST data: -- http2: remove check for !data after it was already dereferenced + --variable %HOME=/home/default + --expand-variable fix@{{HOME}}/.secret + --expand-data "{{fix:trim:url}}" + https://example.com/ - Pointed out by Coverity + Documented. Many new test cases. - Closes #10906 + Co-brainstormed-by: Emanuele Torre + Assisted-by: Jat Satiro + Closes #11346 -- http_proxy: provide missing arg to infof() call +- KNOWN_BUGS: cygwin: make install installs curl-config.1 twice - Pointed out by Coverity + Closes #8839 - Closes #10904 +- KNOWN_BUGS: build for iOS simulator on macOS 13.2 with Xcode 14 -- content_encoding: only do tranfer-encoding compression if asked to + Closes #11215 - To reduce surprises. Update test 387 and 418 accordingly. +- KNOWN_BUGS: cmake outputs: no version information available - Closes #10899 + Closes #11158 -- sws: comparison of unsigned expression < 0 is always false +- KNOWN_BUGS: APOP authentication fails on POP3 - Follow-up to 356dd0b73a75ed6d5 + Closes #10073 - Closes #10903 +- KNOWN_BUGS: hyper is slow -- lib/cmake: add HAVE_WRITABLE_ARGV check + Closes #11203 - Assisted-by: Jakub Zakrzewski - Closes #10896 +Patrick Monnerat (31 Jul 2023) -- configure: don't set HAVE_WRITABLE_ARGV on Windows +- configure, cmake, lib: more form api deprecation - Ref: #10888 - Closes #10896 + Introduce a --enable-form-api configure option to control its inclusion + in builds. The condition name defined for it is CURL_DISABLE_FORM_API. -- vtls: fix build error when proxy-disabled + Form api code is dependent of MIME: configure and CMake handle this + dependency automatically: CMake by making it a dependent option + explicitly, configure by inheriting the MIME value by default and + rejecting explicit incompatible values. - Closes #10901 + "form-api" is now a new hidden test feature. -Stefan Eissing (6 Apr 2023) + Update libcurl modules to respect this option and adjust tests + accordingly. -- tests: increase sws timeout for more robust testing + Closes #9621 - - for https CONNECT forwarding, this was fixed at 5 seconds - which led to spurious CI test failures - - add --keepalive parameter to sws to control this - - let httpserver use 30 seconds +Daniel Stenberg (31 Jul 2023) - Closes #10898 +- mailmap: add Derzsi Dániel -- http2: move HTTP/2 stream vars into local context +Derzsi Dániel (31 Jul 2023) - - remove NGHTTP2 members of `struct HTTP` - - add `void *h2_ctx` to `struct HTTP` - - add `void *h3_ctx` to `struct HTTP` - - separate h2/h3 pointers are needed for eyeballing - - manage local stream_ctx in http implementations +- wolfssl: support loading system CA certificates - Closes #10877 + Closes #11452 -- proxy: http2 proxy tunnel implementation +Viktor Szakats (30 Jul 2023) - - currently only on debug build and when env variable - CURL_PROXY_TUNNEL_H2 is present. - - will ALPN negotiate with the proxy server and switch - tunnel filter based on the protocol negotiated. - - http/1.1 tunnel code moved into cf-h1-proxy.[ch] - - http/2 tunnel code implemented in cf-h2-proxy.[ch] - - tunnel start and ALPN set remains in http_proxy.c - - moving all haproxy related code into cf-haproxy.[ch] +- nss: delete more NSS references - VTLS changes - - SSL filters rely solely on the "alpn" specification they - are created with and no longer check conn->bits.tls_enable_alpn. - - checks on which ALPN specification to use (or none at all) are - done in vtls.c when creating the filter. + Fix the distcheck CI failure and delete more NSS references. - Testing - - added a nghttpx forward proxy to the pytest setup that - speaks HTTP/2 and forwards all requests to the Apache httpd - forward proxy server. - - extending test coverage in test_10 cases - - adding proxy tests for direct/tunnel h1/h2 use of basic auth. - - adding test for http/1.1 and h2 proxy tunneling to pytest + Follow-up to 7c8bae0d9c9b2dfeeb008b9a316117d7b9675175 - Closes #10780 + Reviewed-by: Marcel Raad + Reviewed-by: Daniel Stenberg + Closes #11548 -- vtls and h2 improvements +Daniel Stenberg (29 Jul 2023) - - eliminate receive loop in vtls to fill buffer. This may - lead to partial reads of data which is counter productive - - let http2 instead loop smarter to process pending network - data without transfer switches +- nss: remove support for this TLS library - scorecard improvements - - do not start caddy when only httpd is requested - - allow curl -v to stderr file on --curl-verbose + Closes #11459 - Closes #10891 +Ryan Schmidt (29 Jul 2023) -Daniel Stenberg (6 Apr 2023) +- macOS: fix target detection more -- tests: 1078 1288 1297 use valid IPv4 addresses + Now SCDynamicStoreCopyProxies is called (and the required frameworks are + linked in) on all versions of macOS and only on macOS. Fixes crash due + to undefined symbol when built with the macOS 10.11 SDK or earlier. - With the enhanced URL parser, these tests failed because of their bad - IPv4 use. + CURL_OSX_CALL_COPYPROXIES is renamed to CURL_MACOS_CALL_COPYPROXIES and + is now only defined when SCDynamicStoreCopyProxies will actually be + called. Previously, it was defined when ENABLE_IPV6 was not defined but + SCDynamicStoreCopyProxies is not called in that case. -- urlapi: detect and error on illegal IPv4 addresses + TARGET_OS_OSX is only defined in the macOS 10.12 SDK and later and only + when dynamic targets are enabled. TARGET_OS_MAC is always defined but + means any Mac OS or derivative including macOS, iOS, tvOS, and watchOS. + TARGET_OS_IPHONE means any Darwin OS other than macOS. - Using bad numbers in an IPv4 numerical address now returns - CURLUE_BAD_HOSTNAME. + Follow-up to c73b2f82 - I noticed while working on trurl and it was originally reported here: - https://github.com/curl/trurl/issues/78 + Fixes #11502 + Closes #11516 - Updated test 1560 accordingly. +Daniel Stenberg (29 Jul 2023) - Closes #10894 +- tool_operate: allow SSL_CERT_FILE and SSL_CERT_DIR -- RELEASE-NOTES: synced + ... used at once. -- urlapi: URL encoding for the URL missed the fragment + Reported-by: Gabriel Corona + Fixes #11325 + Closes #11531 - Meaning that it would wrongly still store the fragment using spaces - instead of %20 if allowing space while also asking for URL encoding. +Thomas M. DuBuisson (29 Jul 2023) - Discovered when playing with trurl. +- CI: remove Lift's configuration - Added test to lib1560 to verify the fix. + The Lift tool is being retired. Their site reads: - Closes #10887 + "Sonatype Lift will be retiring on Sep 12, 2023, with its analysis + stopping on Aug 12, 2023." -- rtsp: convert mallocs to dynbuf for RTP buffering + Closes #11541 - Closes #10786 +Nathan Moinvaziri (29 Jul 2023) -- tool_writeout: add URL component variables +- Revert "schannel: reverse the order of certinfo insertions" - Output specific components from the used URL. The following variables - are added for this purpose: + This reverts commit 8986df802db9b5338d9d50a54232ebae4dbcf6dd. - url.scheme, url.user, url.password, url.options, url.host, url.port, - url.path, url.query, url.fragment, url.zoneid + Windows does not guarantee a particular certificate ordering, even + though TLS may have its own ordering/relationship guarantees. Recent + versions of Windows 11 reversed the ordering of ceritifcates returned by + CertEnumCertificatesInStore, therefore this commit no longer works as + initially intended. libcurl makes no guarantees about certificate + ordering if the operating system can't. - Add the following for outputting parts of the "effective URL": + Ref: https://github.com/curl/curl/issues/9706 - urle.scheme, urle.user, urle.password, urle.options, urle.host, urle.port, - urle.path, urle.query, urle.fragment, urle.zoneid + Closes https://github.com/curl/curl/pull/11536 - Added test 423 and 424 to verify. +wangzhikun (29 Jul 2023) - Closes #10853 +- winbuild: improve check for static zlib -Stefan Eissing (4 Apr 2023) + - Check for zlib static library name zlibstatic.lib. -- tests/http: improved httpd detection + zlib's static library has a different name depending on how it was + built. zlibstatic.lib is output by cmake. zlibstat.lib is output by + their pre-generated Visual Studio project files (in the contrib + directory) and defines ZLIB_WINAPI (ie it's meant to use stdcall + instead of cdecl if you end up exporting the zlib functions). - - better error messages when not found/complete - - handling of `--without-test-httpd` + Prior to this change the makefile only checked for the latter. - Reported-by: kwind on github - Fixes #10879 - Closes #10883 + Closes https://github.com/curl/curl/pull/11521 -Daniel Stenberg (4 Apr 2023) +Daniel Stenberg (29 Jul 2023) -- configure: make quiche require quiche_conn_send_ack_eliciting +- configure: use the pkg-config --libs-only-l flag for libssh2 - curl now requires quiche version >= 1.17.1 to be used and this function - was added in this version and makes a convenient check. + ... instead of --libs, as that one also returns -L flags. - This requirement is because this is the lowest quiche version that - supports peer-initiated key updates correctly. + Reported-by: Wilhelm von Thiele + Fixes #11538 + Closes #11539 - Closes #10886 +Viktor Szakats (29 Jul 2023) -Dan Fandrich (1 Apr 2023) +- cmake: support building static and shared libcurl in one go -- unit tests: use the unit test infrastructure better + This patch adds the ability to build a static and shared libcurl library + in a single build session. It also adds an option to select which one to + use when building the curl executable. - Allow UNITTEST_STOP to return the error code, use the fail & abort - macros to indicate test failure and return success instead of fail if - the unit test can't test anything because of missing features at - compile-time. A couple of tests could never fail because they were - overriding the failure return code. + New build options: + - `BUILD_STATIC_LIBS`. Default: `OFF`. + Enabled automatically if `BUILD_SHARED_LIBS` is `OFF`. + - `BUILD_STATIC_CURL`. Default: `OFF`. + Requires `BUILD_STATIC_LIBS` enabled. + Enabled automatically if building static libcurl only. + - `STATIC_LIB_SUFFIX`. Default: empty. + - `IMPORT_LIB_SUFFIX`. Default: `_imp` if implib filename would collide + with static lib name (typically with MSVC) in Windows builds. + Otherwise empty. -- runtests: strip EOL on precheck output on Windows, too + Also: - Precheck failures would show on two lines in the test summary output - otherwise. + - Stop setting the `CURL_STATICLIB` macro via `curl_config.h`, and pass + it directly to the compiler. This also allows to delete a condition + from `tests/server/CMakeLists.txt`. -- tests: move server config files under the pid dir + - Complete a TODO by following the logic used in autotools (also for + `LIBCURL_NO_SHARED`), and set `-DCURL_STATICLIB` in `Cflags:` of + `libcurl.pc` for _static-only_ curl builds. - These files are generated by the test servers and must therefore be - found in the log directory to make them available to only those servers - once multiple test runners are executing in parallel. They must also not - be deleted with the log files, so they are stored in the pidfile - directory. + - Convert an existing CI test to build both shared and static libcurl. - Ref: #10818 - Closes #10875 + Closes #11505 -- runtests: use the ssh key filenames from the sshhelp package +Stefan Eissing (28 Jul 2023) -- tests: move pidfiles and portfiles under the log directory +- CI/awslc: add cache for build awslc library - This is to segregate all files written by a test process into a single - root to allow for future parallel testing. + Closes #11535 - Ref: #10818 - Closes #10874 +- GHA/linux.yml: add caching -- runtests: minor code cleanups + Closes #11532 -- runtests: call processexists() and pidfromfile() +Daniel Stenberg (27 Jul 2023) - rather than duplicating the logic in several places. +- RELEASE-NOTES: synced -Viktor Szakats (31 Mar 2023) + Bump working version to 8.3.0 -- cmake: do not add zlib headers for openssl +- url: remove infof() output for "still name resolving" - Logic copied earlier from wolfSSL. wolfSSL requires zlib headers for its - public headers. OpenSSL does not, so stop adding zlib headers for it. + The message does not help and might get spewed a lot during times. - Follow-up to 1e3319a167d2f32d295603167486e9e88af9bb4e + Reported-by: yushicheng7788 on github + Fixes #11394 + Closes #11529 - Closes #10878 +- KNOWN_BUGS: cygwin: "WARNING: UNPROTECTED PRIVATE KEY FILE!" -Stefan Eissing (31 Mar 2023) + Closes #11244 -- rustls: fix error in recv handling +Stefan Eissing (27 Jul 2023) - - when rustls is told to recieve more TLS data and its internal - plaintext buffers are full, it returns an IOERROR - - avoid receiving TLS data while plaintext is not read empty +- CI: quiche updates - pytest: - - increase curl run timeout when invoking pytest with higher verbosity + - remove quiche from standard `linux` workflow + - add mod_h2 caching to quiche workflow + - rename quiche to quiche-linux + - move version definitions into env section - Closes #10876 + Closes #11528 -- http3: improvements across backends +- http2: disable asssertion blocking OSSFuzz testing - - ngtcp2: using bufq for recv stream data - - internal stream_ctx instead of `struct HTTP` members - for quiche, ngtcp2 and msh3 - - no more QUIC related members in `struct HTTP` - - experimental use of recvmmsg(), disabled by default - - testing on my old debian box shows no throughput improvements. - - leaving it in, but disabled, for future revisit - - vquic: common UDP receive code for ngtcp2 and quiche - - vquic: common UDP send code for ngtcp2 and quiche - - added pytest skips for known msh3 failures - - fix unit2601 to survive torture testing - - quiche: using latest `master` from quiche and enabling large download - tests, now that key change is supported - - fixing test_07_21 where retry handling of starting a stream - was faulty - - msh3: use bufq for recv buffering headers and data - - msh3: replace fprintf debug logging with LOG_CF where possible - - msh3: force QUIC expire timers on recv/send to have more than - 1 request per second served + - not clear how this triggers and it blocks OSSFuzz testing other + things. Since we handle the case with an error return, disabling the + assertion for now seems the best way forward. - Closes #10772 + Fixes #11500 + Closes #11519 -Dan Fandrich (30 Mar 2023) +- http2: fix in h2 proxy tunnel: progress in ingress on sending -- test1471/2: add http as a required feature + - depending on what is tunneled, the proxy may never get invoked for + receiving data explicitly. Not progressing ingress may lead to stalls + due to missed WINDOW_UPDATEs. - curl bails out early with a different error message if http support is - compiled out. + CI: + - add a chache for building mod_h2 - Ref: #10705 + Closes #11527 -- tests: limit return code of unit tests and lib tests +- CI ngtcp2+quictls: use nghttpx cache as in quiche build - Values greater than 125 have special meanings, so cap it there. Unit - tests and lib tests use the number of failures as the return code, so a - large number of failures (such as test 2601 as a torture test) can - exceed this causing the test to be erroneously reported as having - failed. +Jay Satiro (27 Jul 2023) - Ref: #10720 +- bearssl: don't load CA certs when peer verification is disabled -- test1960: point to the correct path for the precheck tool + We already do this for other SSL backends. - Otherwise, it might find the binary in .libs which can cause it to use - the system libcurl which can fail. This error is only visible by - noticing that the test is skipped. + Bug: https://github.com/curl/curl/pull/11457#issuecomment-1644587473 + Reported-by: kyled-dell@users.noreply.github.com - Follow-up to e4dfe6fc + Closes https://github.com/curl/curl/pull/11497 - Ref: #10651 +Daniel Stenberg (26 Jul 2023) -- tests: use the proper %LOGDIR path on two tests +- easy: remove #ifdefs to make code easier on the eye - Follow-up to e7a021e1 + Closes #11525 - Ref: #10818 +Stefan Eissing (26 Jul 2023) -Daniel Stenberg (30 Mar 2023) +- GHA: adding quiche workflow -- rtsp: fix Value stored to 'skip_size' is never read + - adding separate quiche workflow to also build nghttpx server for testing - Pointed out by scan-build + Closes #11517 - Follow-up to 6c6306f3008f2c9b20a64 +Version 8.2.1 (26 Jul 2023) - Closes #10872 +Daniel Stenberg (26 Jul 2023) -Stefan Eissing (30 Mar 2023) +- RELEASE-NOTES: synced -- tests/http: relax connection check in test_07_02 + curl 8.2.1 release - Only 1 connection will be used when curl is slow, happens when - address-sanitized in CI, for example +- THANKS: add contributors from 8.2.1 - Closes #10865 +- docs: provide more see also for cipher options -- http2: flow control and buffer improvements + More cross references. Hide nroff errors. - - use bufq for send/receive of network data - - usd bufq for send/receive of stream data - - use HTTP/2 flow control with no-auto updates to control the - amount of data we are buffering for a stream - HTTP/2 stream window set to 128K after local tests, defined - code constant for now - - elminiating PAUSEing nghttp2 processing when receiving data - since a stream can now take in all DATA nghttp2 forwards + Closes #11513 - Improved scorecard and adjuste http2 stream window sizes - - scorecard improved output formatting and options default - - scorecard now also benchmarks small requests / second +- docs: mark two TLS options for TLS, not SSL - Closes #10771 + Closes #11514 -Dan Fandrich (30 Mar 2023) +Brad Harder (25 Jul 2023) -- runtests: show error message if file can't be written +- curl_multi_wait.3: fix arg quoting to doc macro .BR -- tests: fix remaining servers to run with a dynamic log directory + Closes #11511 - This final commit in the series is sufficient to allow the tests succeed - if $LOGDIR is changed in runtests.pl. +Daniel Stenberg (24 Jul 2023) - Ref: #10818 - Closes #10866 +- RELEASE-NOTES: synced -- tests: fix fake_ntlm to run with a dynamic log directory +Viktor Szakats (24 Jul 2023) - Ref: #10818 +- cmake: update ngtcp2 detection -- tests: fix http servers to run with a dynamic log directory + Replace `OpenSSL` with `quictls` to follow the same change + in the v0.17.0 ngtcp2 release. - Ref: #10818 + Follow-up to e0093b4b732f6495b0fb1cd6747cbfedcdcf63ed -- tests: fix ftpserver to run with a dynamic log directory + Closes #11508 - Ref: #10818 +Stefan Eissing (24 Jul 2023) -- tests: fix C servers to run with a dynamic log directory +- http: VLH, very large header test and fixes - Ref: #10818 + - adding tests using very large passwords in auth + - fixes general http sending to treat h3 like h2, and + not like http1.1 + - eliminate H2_HEADER max definitions and use the commmon + DYN_HTTP_REQUEST everywhere, different limits do not help + - fix http2 handling of requests denied by nghttp2 on send + to immediately report the refused stream -- tests: fix lib tests to run with a dynamic log directory + Closes #11509 - Ref: #10818 +Andrei Rybak (23 Jul 2023) -- tests: fix unit tests to run with a dynamic log directory +- CONTRIBUTE: drop mention of copyright year ranges - Ref: #10818 + Year ranges in copyrights were dropped in commits [1] and [2]. + Verification of year ranges in copyrights was dropped from script + 'scripts/copyright.pl' in commit [3]. However, the corresponding + passages in file 'docs/CONTRIBUTE.md' weren't updated. -- tests: use %LOGDIR to refer to the log directory + Drop mentions of copyright year ranges from 'docs/CONTRIBUTE.md'. - This will allow it be set dynamically. + [1] 2bc1d775f (copyright: update all copyright lines and remove year + ranges, 2023-01-02) + [2] c46761bd8 (tests/http: remove year ranges from copyrights, + 2023-03-14) + [3] 0e293bacb (copyright.pl: cease doing year verifications, 2023-01-28) - Ref: #10818 + Closes #11504 -- runtests: track verification time even if no files to compare +- CONTRIBUTE: fix syntax in commit message description -- getpart: better handle case of file not found + File 'docs/CONTRIBUTE.md' includes a description of how one should write + commit messages in the curl project. Different possible parts of the + message are enclosed in square brackets. One exception is the section + describing how the curl project doesn't use "Signed-off-by" commit + trailers [1], which is enclosed in an opening curly brace paired with a + closing square bracket. -- testcurl: bump version date + Fix the enclosing square brackets in description of "Signed-off-by" + trailers in commit messages in file 'docs/CONTRIBUTE.md'. - It hadn't been updated in 9 years; it's time. + [1] See description of option '--signoff' in Git documentation: + https://git-scm.com/docs/git-commit -- tests: switch to 3-argument open in test suite + Closes #11504 - The perl 2-argument open has been considered not-quite-deprecated since - the 3-argument form was introduced almost a quarter century ago. +Daniel Stenberg (23 Jul 2023) -- tests: silence some Perl::Critic warnings in test suite +- src/mkhelp: strip off escape sequences - Not all warnings are fixed; many are as much stylistic suggestions than - anything and IMHO don't do much to actually improve the code. + At some point the nroff command stopped stripping off escape sequences, + so then this script needs to do the job instead. - Ref: #10818 - Closes #10861 + Reported-by: VictorVG on github + Fixes #11501 + Closes #11503 -- docs: bump the minimum perl version to 5.6 +- KNOWN_BUGS: building for old macOS fails with gcc - It's actually been this way since at least 2012 (when a 3-argument open - was added to runtests.pl). Given the lack of complaints in the interim, - it's safe to call this 23 year old perl version the minimum. + Closes #11441 -- runtests: memoize the getpart* subroutines to speed up access +Jacob Hoffman-Andrews (22 Jul 2023) - The refactored code calls these functions with the same arguments more - often, so this prevents redundant test case file parsing. +- rustls: update rustls-ffi 0.10.0 - Approved-by: Daniel Stenberg - Ref: #10818 - Closes #10833 + This brings in version 0.21.0 of the upstream rustls implementation, + which notable includes support for IP address certificates. -- runtests: remove duplicated feature variables + Closes #10865 - Use the feature map stored in the hash table instead. Most of the - variables were only used only once, to set the value in the hash table. +Brad Harder (22 Jul 2023) - Ref: #10818 +- websocket: rename arguments/variables to match docs -- runtests: also ignore test file problems when ignoring results + Pedantry/semantic-alignment between functions, docs, comments with + respect to websocket protocol code; No functional change intended. - This simplifies error handling in the test verification code and makes - it more consistent. + * "totalsize", "framesize" becomes "fragsize" (we deal in frame fragments). - Ref: #10818 + * "sendflags" becomes "flags" -- runtests: more refactoring for clarity + * use canonical CURL *handle - Ref: #10818 + Closes #11493 -- runtests: don't start servers if -l is given +Jan Macku (21 Jul 2023) -- runtests: fix typos +- bug_report: use issue forms instead of markdown template -- runtests: refactor singletest() into separate functions + Issue forms allow you to define web-like input forms using YAML + syntax. It allows you to guide the reporter to get the required + information. - This takes it from a 1200 line behemoth into something more manageable. - The content and order of the functions is taken almost directly from - singletest() so the diff sans whitespace is quite short. + Signed-off-by: Jan Macku + Closes #11474 - Ref: #10818 +Daniel Stenberg (21 Jul 2023) -- runtests: refactor singletest() into distinct sections +- TODO: Obey Retry-After in redirects - Namely: - - Verify that this test case should be run - - Start the servers needed to run this test case - - Check that test environment is fine to run this test case - - Prepare the test environment to run this test case - - Run the test command - - Clean up after test command - - Verify test succeeded + (remove "Set custom client ip when using haproxy protocol" which was + shipped in 8.2.0) - Ref: #10818 + Mentioned-by: Yair Lenga + Closes #11447 -- runtests: stop copying a few arrays where not needed +- RELEASE-NOTES: synced - Unlike some other languages that just copy a pointer, perl copies the - entire array contents which takes time for a large array. +Oliver Roberts (21 Jul 2023) - Ref: #10818 +- amissl: fix AmiSSL v5 detection -- runtests: reduce redundant calls to getpart/getpartattr + Due to changes in the AmiSSL SDK, the detection needed adjusting. - These functions scan through the entire test file every time to find the - right section, so they can be slow for large test files. + Closes #11477 - Ref: #10818 +Alois Klink (21 Jul 2023) -- tests: document that the unittest keyword is special +- unittest/makefile: remove unneeded unit1621_LDADD - Also, add other features that were missing. + The `unit1621_LDADD` variable has the exact same value as the `LDADD` + flag in `Makefile.am`, except without `@LDFLAGS@ @LIBCURL_LIBS@`. -Stefan Eissing (30 Mar 2023) + This was originally added by [98e6629][], but I can't see any reason + why it exists, so we should remove it to clean things up. -- docs: add documentation for bufq + [98e6629]: https://github.com/curl/curl/commit/98e6629154044e4ab1ee7cff8351c7 + ebcb131e88 - Closes #10869 + Closes #11494 -Daniel Stenberg (30 Mar 2023) +- unittest/makefile: remove unneeded unit1394_LDADD -- RELEASE-NOTES: synced + These custom `unit1394_LDADD` and similar automake overrides are no + longer neded. They were originally added by added by [8dac7be][] for + metalink support, but are no longer after [265b14d][] removed metalink. -Matt Jolly (30 Mar 2023) + [8dac7be]: https://github.com/curl/curl/commit/8dac7be438512a8725d3c71e9139bd + fdcac1ed8c + [265b14d]: https://github.com/curl/curl/commit/265b14d6b37c4298bd5556fabcbc37 + d36f911693 -- hostip: refuse to resolve the .onion TLD + Closes #11494 - RFC 7686 states that: +- cmake: add `libcurlu`/`libcurltool` for unit tests - > Applications that do not implement the Tor - > protocol SHOULD generate an error upon the use of .onion and - > SHOULD NOT perform a DNS lookup. + Add a `libcurlu`/`libcurltool` static library that is compiled only for + unit tests. We use `EXCLUDE_FROM_ALL` to make sure that they're not + built by default, they're only built if unit tests are built. - Let's do that. + These libraries allow us to compile every unit test with CMake. - https://www.rfc-editor.org/rfc/rfc7686#section-2 + Closes #11446 - Add test 1471 and 1472 to verify +Daniel Stenberg (21 Jul 2023) - Fixes #543 - Closes #10705 +- test979: test -u with redirect to (the same) absolute host -Philip Heiduck (30 Mar 2023) + Verifies #11492 -- GHA: update ngtcp2-*.yml to v0.10.0 +- transfer: do not clear the credentials on redirect to absolute URL - Closes #10612 + Makes test 979 work. Regression shipped in 8.2.0 from commit + dd4d1a26959f63a2c -Stefan Eissing (30 Mar 2023) + Fixes #11486 + Reported-by: Cloudogu Siebels + Closes #11492 -- tests/http: fix log formatting on wrong exit code +Jon Rumsey (20 Jul 2023) - Closes #10868 +- os400: correct EXPECTED_STRING_LASTZEROTERMINATED -Daniel Stenberg (30 Mar 2023) + Correct EXPECTED_STRING_LASTZEROTERMINATED to account for + CURLOPT_HAPROXY_CLIENT_IP which requires EBCDIC to ASCII conversion when + passed into curl_easy_setopt(). -- spellcheck.words: unify the AWS-LC spelling + Closes #11476 - Follow-up to 34ef4fab22d93 +Oliver Roberts (20 Jul 2023) - Closes #10867 +- amissl: add missing signal.h include -Jim King (30 Mar 2023) + In some environments, signal.h is already included, but not in others + which cause compilation to fail, so explictly include it. -- openssl: interop with AWS-LC + Closes #11478 - * Configure changes to detect AWS-LC - * CMakeLists.txt changes to detect AWS-LC - * Compile-time branches needed to support AWS-LC - * Correctly set OSSL_VERSION and report AWS-LC release number - * GitHub Actions script to build with autoconf and cmake against AWS-LC +- amigaos: fix sys/mbuf.h m_len macro clash - AWS-LC is a BoringSSL/OpenSSL derivative - For more information see https://github.com/awslabs/aws-lc/ + The updated Curl_http_req_make and Curl_http_req_make2 functions spawned + a parameter called m_len. The AmigaOS networking headers, derived from + NetBSD, contain "#define m_len m_hdr.mh_len" which clashes with + this. Since we do not actually use mbuf, force the include file to be + ignored, removing the clash. - Closes #10320 + Closes #11479 -Viktor Szakats (30 Mar 2023) +Daniel Stenberg (20 Jul 2023) -- cmake: picky-linker fixes for openssl, ZLIB, H3 and more +- socks: print ipv6 address within brackets - - fix HTTP/3 support detection with OpenSSL/quictls built with ZLIB. - (Requires curl be built with ZLIB option also.) + Fixes #11483 + Closes #11484 - - fix HTTP/3 support detection with OpenSSL/quictls/LibreSSL and `ld` - linker on Windows. +Christian Schmitz (20 Jul 2023) - - fix HTTP/3 support detection with wolfSSL to automatically add - `ws2_32` to the lib list on Windows. For all linkers. +- libcurl-errors.3: add CURLUE_OK - - reposition ZLIB (and other compression) detection _after_ TLS - detection, but before calling HTTP/3-support detection via - `CheckQuicSupportInOpenSSL`. + Closes #11488 - May be a regression from ebef55a61df0094b9790710a42f63c48e7de3c13 - May fix #10832 (Reported-by: Micah Snyder) +Oliver Roberts (20 Jul 2023) - This also seems to fix an odd case, where OpenSSL/quictls is correctly - detected, but its header path is not set while compiling, breaking - build at `src/curl_ntlm_core.c`. Reason for this remains undiscovered. +- cfilters: rename close/connect functions to avoid clashes - - satisfy "picky" linkers such as `ld` with MinGW, that are highly - sensitive to lib order, by also adding brotli to the beginning of the - lib list. + Rename `close` and `connect` in `struct Curl_cftype` for + consistency and to avoid clashes with macros of the same name + (the standard AmigaOS networking connect() function is implemented + via a macro). - - satisfy "picky" linkers by adding certain Windows systems libs to - the lib list for OpenSSL/LibreSSL. (Might need additional ones for - other forks, such as `pthread` for BoringSSL.) + Closes #11491 - Note: It'd make sense to _always_ add `ws2_32`, `crypt32` (except - Windows App targets perhaps?), `bcrypt` (except old-mingw!) on Windows - at this point. They are almost always required, and if some aren't, - they are ignored by the linker with no effect on final binaries. +Stefan Eissing (20 Jul 2023) - Closes #10857 +- http2: fix regression on upload EOF handling -Stefan Eissing (30 Mar 2023) + - a regression introduced by c9ec85121110d7cbbbed2990024222c8f5b8afe5 + where optimization of small POST bodies leads to a new code path + for such uploads that did not trigger the "done sending" event + - add triggering this event for early "upload_done" situations -- vlts: use full buffer size when receiving data if possible + Fixes #11485 + Closes #11487 + Reported-by: Aleksander Mazur - SSL backends like OpenSSL/wolfSSL and other return the content of one - TLS record on read, but usually there are more available. +Daniel Stenberg (19 Jul 2023) - Change the vtls cfilter recv() function to fill the given buffer until a - read would block. +- configure: check for nghttp2_session_get_stream_local_window_size - Closes #10736 + The http2 code uses it now. Introduced in nghttp2 1.15.0 (Sep 2016) -dengjfzh on github (30 Mar 2023) + Fixes #11470 + Reported-by: Paul Howarth + Closes #11473 -- rtsp: skip malformed RTSP interleaved frame data +Stefan Eissing (19 Jul 2023) - Some IP cameras send malformed RTSP interleaved frames sometimes, which - can cause curl_easy_perform return 1 (CURLE_UNSUPPORTED_PROTOCOL). This - change attempts to skip clearly incorrect RTSP interleaving frame data. +- quiche: fix segfault and other things - Closes #10808 + - refs #11449 where a segfault is reported when IP Eyeballing did + not immediately connect but made several attempts + - The transfer initiating the eyeballing was initialized too early, + leadding to references to the filter instance that was then + replaced in the subsequent eyeball attempts. That led to a use + after free in the buffer handling for the transfer + - transfers are initiated now more lazy (like in the ngtcp2 filter), + when the stream is actually opened + - suppress reporting on quiche event errors for "other" transfers + than the current one to not fail a transfer due to faults in + another one. + - revert recent return value handling for quiche_h3_recv_body() + to not indicate an error but an EAGAIN situation. We wish quiche + would document what functions return. -Stefan Eissing (30 Mar 2023) + Fixes #11449 + Closes #11469 + Reported-by: ウさん -- lib: add `bufq` and `dynhds` +Daniel Stenberg (19 Jul 2023) - Adding `bufq`: - - at init() time configured to hold up to `n` chunks of `m` bytes each. - - various methods for reading from and writing to it. - - `peek` support to get access to buffered data without copy - - `pass` support to allow buffer flushing on write if it becomes full - - use case: IO buffers for dynamic reads and writes that do not blow up - - distinct from `dynbuf` in that: - - it maintains a read position - - writes on a full bufq return CURLE_AGAIN instead of nuking itself - - Init options: - - SOFT_LIMIT: allow writes into a full bufq - - NO_SPARES: free empty chunks right away - - a `bufc_pool` that can keep a number of spare chunks to - be shared between different `bufq` instances +- hostip: return IPv6 first for localhost resolves - Adding `dynhds`: - - a straightforward list of name+value pairs as used for HTTP headers - - headers can be appended dynamically - - headers can be removed again - - headers can be replaced - - headers can be looked up - - http/1.1 formatting into a `dynbuf` - - configured at init() with limits on header counts and total string - sizes - - use case: pass a HTTP request or response around without being version - specific - - express a HTTP request without a curl easy handle (used in h2 proxy - tunnels) - - future extension possibilities: - - conversions of `dynhds` to nghttp2/nghttp3 name+value arrays + Fixes #11465 + Reported-by: Chilledheart on github + Closes #11466 - Closes #10720 +Harry Sintonen (19 Jul 2023) -- pytest: improvements for suitable curl and error output +- tool: fix tool_seek_cb build when SIZEOF_CURL_OFF_T > SIZEOF_OFF_T - - will check built curl for http and https support and - skip all tests if not there - - will dump stdout/stderr/trace output on errored responses + - a variable was renamed, and some use of it wasn't. this fixes the + build. - Closes #10829 + Closes #11468 -Daniel Stenberg (29 Mar 2023) +Stefan Eissing (19 Jul 2023) -- lib: use correct printf flags for sockets and timediffs +- quiche: fix lookup of transfer at multi - Introduces CURL_FORMAT_SOCKET_T for outputting socket numbers. + - refs #11449 where weirdness in quiche multi connection tranfers was + observed + - fixes lookup of transfer for a quiche event to take the connection + into account + - formerly, a transfer with the same stream_id, but on another connection + could be found - Fixes #10737 - Reported-by: Gisle Vanem - Closes #10855 + Closes #11462 -- telnet: make MSVC ignore warning for assignment within conditional +Daniel Stenberg (19 Jul 2023) - Follow-up to d92a5007b60e0af7d +- RELEASE-NOTES: synced - Closes #10859 + bump to 8.2.1 -- ws: handle reads before EAGAIN better +John Haugabook (19 Jul 2023) - Reported-by: simplerobot on github - Fixes #10831 - Closes #10856 +- ciphers.d: put URL in first column -- test1592: add flaky keyword + This makes the URL turn into a link properly when "webified". - Closes #10860 + Fixes https://github.com/curl/curl-www/issues/270 + Closes #11464 -Frank Gevaerts (28 Mar 2023) +Version 8.2.0 (19 Jul 2023) -- lib/sha256.c: typo fix in comment (duplicated "is available") +Daniel Stenberg (19 Jul 2023) - Closes #10851 +- RELEASE-NOTES: synced -Arne Soete (28 Mar 2023) + 8.2.0 release -- tests: update tests/httpd references to tests/http +- THANKS-filter: strip out "GitHub" - tests/httpd was renamed to tests/http in #10654. This patch updates some - references in the README +- THANKS: add contributors from 8.2.0 - Closes #10854 +- RELEASE-PROCEDURE.md: adjust the release dates -Kamil Dudka (28 Mar 2023) +Stefan Eissing (17 Jul 2023) -- telnet: simplify the implementation of str_is_nonascii() +- quiche: fix defects found in latest coverity report - There is no need to traverse the string twice. + Closes #11455 - Closes #10852 +Daniel Stenberg (17 Jul 2023) -Frank Gevaerts (28 Mar 2023) +- quiche: avoid NULL deref in debug logging -- curl_easy_getinfo.3: typo fix (duplicated "from the") + Coverity reported "Dereference after null check" - Closes #10850 + If stream is NULL and the function exits, the logging must not deref it. -Philip Heiduck (28 Mar 2023) + Closes #11454 -- wolfssl.yml: bump to version 5.6.0 +Stefan Eissing (17 Jul 2023) - Closes #10843 +- http2: treat initial SETTINGS as a WINDOW_UPDATE -Daniel Stenberg (28 Mar 2023) + - refs #11426 where spurious stalls on large POST requests + are reported + - the issue seems to involve the following + * first stream on connection adds up to 64KB of POST + data, which is the max default HTTP/2 stream window size + transfer is set to HOLD + * initial SETTINGS from server arrive, enlarging the stream + window. But no WINDOW_UPDATE is received. + * curl stalls + - the fix un-HOLDs a stream on receiving SETTINGS, not + relying on a WINDOW_UPDATE from lazy servers -- RELEASE-NOTES: synced + Closes #11450 -Ronan Pigott (28 Mar 2023) +Daniel Stenberg (17 Jul 2023) -- docs/cmdline-opts: document the dotless config path +- ngtcp2: assigning timeout, but value is overwritten before used - The real xdg config path is $XDG_CONFIG_HOME/curlrc, without the dot. - The dotless name seems preferable, so let's match the documentation to - the behavior. + Reported by Coverity - Closes #10849 + Closes #11453 -Daniel Stenberg (28 Mar 2023) +- krb5: add typecast to please Coverity -- HTTP-COOKIES.md: mention the #HttpOnly_ prefix +Derzsi Dániel (16 Jul 2023) - Fixes #10847 - Reported-by: Harry Sintonen - Closes #10848 +- wolfssl: support setting CA certificates as blob -- dynbuf: never allocate larger than "toobig" + Closes #11445 - As dynbufs always have a fixed maximum size which they are not allowed - to grow larger than, making sure that it never allocates a larger buffer - makes sure the buffer does not allocate memory that will never be used. +- wolfssl: detect when TLS 1.2 support is not built into wolfssl - Closes #10845 + Closes #11444 -- ftplistparser: replace realloc with dynbuf +Graham Campbell (15 Jul 2023) - Closes #10844 +- CI: bump nghttp2 from 1.55.0 to 1.55.1 -- ftplistparser: use ISDIGIT() + Closes #11442 - Closes #10844 +Daniel Stenberg (15 Jul 2023) -- ftplistparser: move out private data from public struct +- curl: return error when asked to use an unsupported HTTP version - The public 'curl_fileinfo' struct contained three fields that are for - internal purposes only. This change makes them unused in the public - struct. + When one of the following options are used but the libcurl in use does + not support it: - The new private struct fields are also renamed to make this separation - more obvious internally. + --http2 + --http2-prior-knowledge + --proxy-http2 - Closes #10844 + Closes #11440 -- openssl: fix indents - white space edits only +Chris Paulson-Ellis (14 Jul 2023) - Closes #10840 +- cf-socket: don't bypass fclosesocket callback if cancelled before connect -- url: remove call to Curl_llist_destroy in Curl_close + After upgrading to 8.1.2 from 7.84.0, I found that sockets were being + closed without calling the fclosesocket callback if a request was + cancelled after the associated socket was created, but before the socket + was connected. This lead to an imbalance of fopensocket & fclosesocket + callbacks, causing problems with a custom event loop integration using + the multi-API. - A list that is created with a NULL "destructor" does not need to be - destroyed. Not calling it is faster than calling it. + This was caused by cf_socket_close() calling sclose() directly instead + of calling socket_close() if the socket was not active. For regular TCP + client connections, the socket is activated by cf_socket_active(), which + is only called when the socket completes the connect. - Closes #10846 + As far as I can tell, this issue has existed since 7.88.0. That is, + since the code in question was introduced by: + commit 71b7e0161032927cdfb4e75ea40f65b8898b3956 + Author: Stefan Eissing + Date: Fri Dec 30 09:14:55 2022 +0100 -- multi: remove PENDING + MSGSENT handles from the main linked list + lib: connect/h2/h3 refactor - As they are not driving transfers or any socket activity, the main loop - does not need to iterate over these handles. A performance improvement. + Closes #11439 - They are instead only held in their own separate lists. +Daniel Stenberg (13 Jul 2023) - 'data->multi' is kept a pointer to the multi handle as long as the easy - handle is actually part of it even when the handle is moved to the - pending/msgsent lists. It needs to know which multi handle it belongs - to, if for example curl_easy_cleanup() is called before the handle is - removed from the multi handle. +- tool_parsecfg: accept line lengths up to 10M - Alll 'data->multi' pointers of handles still part of the multi handle - gets cleared by curl_multi_cleanup() which "orphans" all previously - attached easy handles. + Bumped from 100K set in 47dd957daff9 - This is take 2. The first version was reverted for the 8.0.1 release. + Reported-by: Antoine du Hamel + Fixes #11431 + Closes #11435 - Assisted-by: Stefan Eissing - Closes #10801 +Stefan Eissing (13 Jul 2023) -Stefan Eissing (26 Mar 2023) +- CI: brew fix for openssl in default path -- tests/http: add timeout to running curl in test cases + If brew install/update links openssl into /usr/local, it will be found + before anything we add with `-isystem path` to CPP/LDLFAGS. Get rid of + that by unlinking the keg. - - we had a CI case once where `curl` seemingly did not - return and it was hard to guess what happened. - - make curl execution in test cases time out after 60 seconds + Fixes #11413 + Closes #11436 - Closes #10783 +Daniel Stenberg (13 Jul 2023) -Daniel Stenberg (26 Mar 2023) +- RELEASE-NOTES: synced -- RELEASE-PROCEDURE: update to new schedule +Ondřej Koláček (13 Jul 2023) - Ref: https://curl.se/mail/lib-2023-03/0062.html +- sectransp: fix EOF handling - Assisted-by: Andy Alt - Assisted-by: Dan Frandrich + Regression since the large refactor from 2022 - Closes #10827 + Closes #11427 -Patrick Monnerat (26 Mar 2023) +Daniel Stenberg (13 Jul 2023) -- doc: curl_mime_init() strong easy handle binding has been relaxed in 7.87.0 +- checksrc: quote the file name to work with "funny" letters - Reported-by: Chloe Kudryavtsev - Fixes #10834 - Closes #10835 + Closes #11437 -Jay Satiro (25 Mar 2023) +Karthikdasari0423 (13 Jul 2023) -- CURLOPT_WRITEFUNCTION.3: fix typo +- HTTP3.md: ngtcp2 updated to v0.17.0 and nghttp3 to v0.13.0 - Reported-by: Osaila@users.noreply.github.com + Follow-up to e0093b4b732f6 - Fixes https://github.com/curl/curl/issues/10839 + Closes #11433 -Dan Fandrich (24 Mar 2023) +Daniel Stenberg (13 Jul 2023) -- CI: skip some more builds when possible +- CURLOPT_MIMEPOST.3: clarify what setting to NULL means - When a commit only contains tests, documentation, or cmake files, skip - those builds that aren't affected by those. + Follow-up to e08382a208d4e480 - The file filters available on the CI services don't seem to allow - skipping individual jobs, only the entire workflow, so we can't get any - more fine-grained than this. + Closes #11430 -- CI: add and adjust labeler match patterns +Tatsuhiro Tsujikawa (12 Jul 2023) - Allow cmdline tool alongside other labels. +- ngtcp2: build with 0.17.0 and nghttp3 0.13.0 -Kai Pastor (25 Mar 2023) + - ngtcp2_crypto_openssl was renamed to ngtcp2_crypto_quictls. -- CMake: make config version 8 compatible with 7 + Closes #11428 - Reviewed-by: Jakub Zakrzewski - Closes #10819 +- CI: Bump ngtcp2, nghttp3, and nghttp2 -Daniel Stenberg (24 Mar 2023) + Closes #11428 -- RELEASE-NOTES: synced +James Fuller (11 Jul 2023) - Bumped version-in-progress to 8.1.0 +- example/maxconnects: set maxconnect example -- GHA: add a memory-sanitizer job + Closes #11343 - Closes #10815 +Pontakorn Prasertsuk (11 Jul 2023) -Dan Fandrich (23 Mar 2023) +- http2: send HEADER & DATA together if possible -- CI: fix brew retries on GHA + Closes #11420 - The fix in the previous commit was complete for Cirrus but accidentally - left off a part for GHA. +Daniel Stenberg (11 Jul 2023) - Follow-up to c2b7249d +- CI: use wolfSSL 5.6.3 in builds -- CI: skip Azure for more commits which change only GHA + No using master anymore -Daniel Stenberg (23 Mar 2023) + Closes #11424 -- cmake: set SONAME for SunOS too +SaltyMilk (11 Jul 2023) - Provided-by: Brian Lund +- fopen: optimize - Closes #10816 + Closes #11419 -Stefan Eissing (23 Mar 2023) +Daniel Stenberg (11 Jul 2023) -- ngtcp2: adjust config and code checks for ngtcp2 without nghttp3 +- cmake: make use of snprintf - - make configure show on HTTP3 feature that both ngtcp2 and nghttp3 - are in play - - define ENABLE_QUIC only when USE_NGTCP2 and USE_NGHTTP3 are defined - - add USE_NGHTTP3 in the ngtcp2 implementation + Follow-up to 935b1bd4544a23a91d68 - Fixes #10793 - Closes #10821 + Closes #11423 -Daniel Stenberg (23 Mar 2023) +Stefan Eissing (11 Jul 2023) -- data.d: emphasize no conversion +- macOS: fix taget detection - When asking curl to send a POST, curl does not encode or change the data. + - TARGET_OS_OSX is not always defined on macOS + - this leads to missing symbol Curl_macos_init() + - TargetConditionals.h seems to define these only when + dynamic targets are enabled (somewhere?) + - this PR fixes that on my macOS 13.4.1 + - I have no clue why CI builds worked without it - Ref: #10820 - Closes #10823 + Follow-up to c7308592fb8ba213fc2c1 + Closes #11417 -- server/getpart: clear the buffer before load +Stan Hu (9 Jul 2023) - Fixes msan warnings: +- hostip.c: Move macOS-specific calls into global init call - ==54195==WARNING: MemorySanitizer: use-of-uninitialized-value - #0 0x55ece35e57cb in line_length /home/runner/work/curl/curl/tests/server - /getpart.c:111:25 - #1 0x55ece35e3b83 in readline /home/runner/work/curl/curl/tests/server/ge - tpart.c:164:24 - #2 0x55ece35e0269 in getpart /home/runner/work/curl/curl/tests/server/get - part.c:347:18 - #3 0x55ece36180b6 in parse_servercmd /home/runner/work/curl/curl/tests/se - rver/sws.c:283:13 + https://github.com/curl/curl/pull/7121 introduced a macOS system call + to `SCDynamicStoreCopyProxies`, which is invoked every time an IP + address needs to be resolved. - Closes #10822 + However, this system call is not thread-safe, and macOS will kill the + process if the system call is run first in a fork. To make it possible + for the parent process to call this once and prevent the crash, only + invoke this system call in the global initialization routine. -- ntlm: clear lm and nt response buffers before use + In addition, this change is beneficial because it: - To avoid the risk of MemorySanitizer: use-of-uninitialized-value + 1. Avoids extra macOS system calls for every IP lookup. + 2. Consolidates macOS-specific initialization in a separate file. - Closes #10814 + Fixes #11252 + Closes #11254 -- digest: clear target buffer +Daniel Stenberg (9 Jul 2023) - Closes #10814 +- docs: use a space after RFC when spelling out RFC numbers -Douglas R. Reno (22 Mar 2023) + Closes #11382 -- cmake: bring in the network library on Haiku. +Margu (9 Jul 2023) - When cross-compiling for Haiku, the networking library needs to be - brought in. Without this, an unknown type of "Error" is reported in - lib/curl_setup_once.h. +- imap-append.c: update to make it more likely to work - This is also needed when using CMake natively on Haiku to build libcurl. + Fixes #10300 + Closes #11397 - Fixes #10296 - Closes #10792 +Emanuele Torre (9 Jul 2023) -Daniel Stenberg (22 Mar 2023) +- tool_writeout_json: fix encoding of control characters -- runtests: die if curl version can be found + Control characters without a special escape sequence e.g. %00 or %06 + were being encoded as "u0006" instead of "\u0006". - Closes #10813 + Ref: https://github.com/curl/trurl/pull/214#discussion_r1257487858 + Closes #11414 -Stefan Eissing (22 Mar 2023) +Stefan Eissing (9 Jul 2023) -- multi: add handle asserts in DEBUG builds +- http3/ngtcp2: upload EAGAIN handling - For GOOD_EASY_HANDLE and GOOD_MULTI_HANDLE checks + - refs #11389 where IDLE timeouts on upload are reported + - reword ngtcp2 expiry handling to apply to both send+recv + calls into the filter + - EAGAIN uploads similar to the recent changes in HTTP/2, e.g. + report success only when send data was ACKed. + - HOLD sending of EAGAINed uploads to avoid cpu busy loops + - rename internal function for consistency with HTTP/2 + implementation - - allow NULL pointers to "just" return an error as before - - fail hard on nun-NULL pointers that no longer show the MAGICs + Fixes #11389 + Closes #11390 - Closes #10812 +Brian Nixon (9 Jul 2023) -Jon Rumsey (22 Mar 2023) +- tool_easysrc.h: correct `easysrc_perform` for `CURL_DISABLE_LIBCURL_OPTION` -- gskit: various compile errors in OS400 + Closes #11398 - Various compile failures in gskit.c; +Daniel Stenberg (9 Jul 2023) - - pipe_ssloverssl() needs Curl_easy data parameter for - Curl_conn_cf_get_socket(cf, data) - - key_passwd is in ssl_config, not conn_config - - close_on() has 2 parameters, not 4 - - getsockopt() needs to call Curl_conn_cf_get_socket(), not - cxn->sock[FIRSTSOCKET] +- RELEASE-NOTES: synced - Fixes #10799 - Closes #10800 +- transfer: clear credentials when redirecting to absolute URL -Daniel Stenberg (22 Mar 2023) + Make sure the user and password for the second request is taken from the + redirected-to URL. -- tool_operate: pass a long as CURLOPT_HEADEROPT argument + Add test case 899 to verify. - Closes #10798 + Reported-by: James Lucas + Fixes #11410 + Closes #11412 -- GHA: run all linux test jobs with valgrind +Stefan Eissing (8 Jul 2023) - Closes #10798 +- hyper: fix EOF handling on input -- GHA-linux: add an address-sanitizer build + We ran out of disc space due to an infinite loop with debug logging - Closes #10810 + Fixes #11377 + Closes #11385 + Reported-by: Dan Fandrich -Version 8.0.1 (20 Mar 2023) +- http2: raise header limitations above and beyond -Daniel Stenberg (20 Mar 2023) + - not quite to infinity + - rewrote the implementation of our internal HTTP/1.x request + parsing to work with very large lines using dynbufs. + - new default limit is `DYN_HTTP_REQUEST`, aka 1MB, which + is also the limit of curl's general HTTP request processing. -- RELEASE-NOTES: synced + Fixes #11405 + Closes #11407 - curl 8.0.1 +Juan Cruz Viotti (8 Jul 2023) -- Revert "multi: remove PENDING + MSGSENT handles from the main linked list" +- curl_easy_nextheader.3: add missing open parenthesis examples - This reverts commit f6d6f3ce01e377932f1ce7c24ee34d45a36950b8. + Closes #11409 + Signed-off-by: Juan Cruz Viotti - The commits caused issues in the 8.0.0 release. Needs a retake. +Dan Fandrich (7 Jul 2023) - Reported-by: Kamil Dudka - Closes #10795 +- CI: enable verbose test output on pytest -- include/curl/curlver.h: bump to 8.0.1 + This shows individual pass/fail status on tests and makes this output + consistent with other jobs' pytest invocations. -Version 8.0.0 (20 Mar 2023) +Stefan Eissing (28 Jun 2023) -Daniel Stenberg (20 Mar 2023) +- http2: fix crash in handling stream weights -- RELEASE-NOTES: synced + - Delay the priority handling until the stream has been opened. - The curl 8.0.0 release + - Add test2404 to reproduce and verify. -- THANKS: from the 8.0.0 release + Weights may change "on the run", which is why there are checks in + general egress handling. These must not trigger when the stream has not + been opened yet. -- scripts/delta: fix "ambiguous argument" when used in branches + Reported-by: jbgoog@users.noreply.github.com -- SECURITY-PROCESS.md: Busy-loops are not security problems + Fixes https://github.com/curl/curl/issues/11379 + Closes https://github.com/curl/curl/pull/11384 - Closes #10790 +- tests/http: Add mod_h2 directive `H2ProxyRequests` -Stefan Eissing (17 Mar 2023) + master of mod_h2 now requires H2ProxyRequests directives for forward + proxying with HTTP/2 to work. -- tests/http: do not save files for downloads in scorecard testing + Ref: https://github.com/icing/mod_h2/commit/3897a7086 - Closes #10788 + Closes https://github.com/curl/curl/pull/11392 -Daniel Stenberg (17 Mar 2023) +Dan Fandrich (28 Jun 2023) -- cf-socket: use port 80 when resolving name for local bind +- CI: make Appveyor job names unique - It turns out c-ares returns an error when asked to resolve a host name with - ares_getaddrinfo using port number 0. + Two otherwise identical mingw-w64 jobs now have their differing compiler + versions mentioned in their names. - Reported as a c-ares bug here: https://github.com/c-ares/c-ares/issues/517 +Sheshadri.V (25 Jun 2023) - The work-around is to simply use port 80 instead, as the number typically doe - s - not make a difference and a non-zero number works for c-ares. +- curl.h: include for vxworks - Fixes #10759 - Reported-by: Matt Jolly - Closes #10789 + Closes #11356 -- curl.h: require gcc 12.1 for the deprecation magic +Dan Fandrich (24 Jun 2023) - Reported-by: kchow-FTNT on github - Fixes #10726 - Closes #10784 +- CI: enable parallel make in more builds -- Revert "rtsp: use dynbuf instead of custom reallocs" + Most CI services provide at least two cores, so enable parallel make + jobs to take advantage of that for builds. Some dependencies aren't safe + to build in parallel so leave those as-is. Also, rename a few + workflows to eliminate duplicate names and provide a better idea what + they're about. - This reverts commit 1b9ea3239d22147e00d8 because of OSS-fuzz reports. - I'll do another take after the pending release. +- CI: don't install impacket if tests are not run - Closes #10785 + It just wastes time and bandwidth and isn't even used. -- test422: verify --next used without a prior URL +divinity76 (24 Jun 2023) - Closes #10782 +- configure: the --without forms of the options are also gone -- tool_getparam: error if --next is used without a prior URL + --without-darwin-ssl and --without-metalink - Reported-by: 積丹尼 Dan Jacobson - Ref: https://github.com/curl/curl/pull/10769#discussion_r1137895629 + Closes #11378 - Closes #10782 +Daniel Stenberg (23 Jun 2023) -- libssh: use dynbuf instead of realloc +- configure: add check for ldap_init_fd - When building lines to show for SFTP directory listings. + ... as otherwise the configure script will say it is OpenLDAP in the + summary, but not set the USE_OPENLDAP define, therefor not using the + intended OpenLDAP code paths. - Closes #10778 + Regression since 4d7385446 (7.85.0) + Fixes #11372 + Closes #11374 + Reported-by: vlkl-sap on github -- lib2305: deal with CURLE_AGAIN +Michał Petryka (23 Jun 2023) - The test does a slightly ugly busy-loop for this case but should be - managable due to it likely being a very short moment. +- cmake: stop CMake from quietly ignoring missing Brotli - Mention CURLE_AGAIN in curl_ws_recv.3 + The CMake project was set to `QUIET` for Brotli instead of + `REQUIRED`. This makes builds unexpectedly ignore missing Brotli even + when `CURL_BROTLI` is enabled. - Fixes #10760 - Reported-by: Jay Satiro - Closes #10781 + Closes #11376 -- rtsp: use dynbuf instead of custom reallocs +Emanuele Torre (22 Jun 2023) - For the RTP buffering. +- docs: add more .IP after .RE to fix indentation of generate paragraphs - Closes #10776 + follow-up from 099f41e097c030077b8ec078f2c2d4038d31353b -- libssh2: remove unused variable from libssh2's struct + I just thought of checking all the other files with .RE, and I found 6 + other files that were missing .IP at the end. - Closes #10777 + Closes #11375 -- RELEASE-NOTES: synced +Stefan Eissing (22 Jun 2023) -- multi: remove PENDING + MSGSENT handles from the main linked list +- http2: h2 and h2-PROXY connection alive check fixes - As they are not driving transfers or any socket activity, the main loop - does not need to iterate over these handles. A performance improvement. + - fix HTTP/2 check to not declare a connection dead when + the read attempt results in EAGAIN + - add H2-PROXY alive check as for HTTP/2 that was missing + and is needed + - add attach/detach around Curl_conn_is_alive() and remove + these in filter methods + - add checks for number of connections used in some test_10 + proxy tunneling tests - They are instead only held in their own separate lists. + Closes #11368 - Assisted-by: Stefan Eissing - Ref: #10743 - Closes #10762 +- http2: error stream resets with code CURLE_HTTP2_STREAM -- multi: turn link/unlinking easy handles into dedicated functions + - refs #11357, where it was reported that HTTP/1.1 downgrades + no longer works + - fixed with suggested change + - added test_05_03 and a new handler in the curltest module + to reproduce that downgrades work -- http_aws_sigv4: fix scan-build "value stored to 'ret' is never read" + Fixes #11357 + Closes #11362 + Reported-by: Jay Satiro - Follow-up to 495d09810aa9a +Daniel Stenberg (22 Jun 2023) - Closes #10766 +- connect-timeout.d: mention that the DNS lookup is included -- lib: skip Curl_llist_destroy calls + Closes #11370 - Linked lists themselves do not carry any allocations, so for the lists - that do not have have a set destructor we can just skip the - Curl_llist_destroy() call and save CPU time. +Emanuele Torre (22 Jun 2023) - Closes #10764 +- quote.d: fix indentation of generated paragraphs -- lib643: LIB644 is never defined, this is dead code + quote.d was missing a .IP at the end which caused the paragraphs + generated for See-also, Multi, and Example to not be indented correctly. - Closes #10765 + I also remove a redundant "This option can be used multiple times.", and + replaced .IP "item" with .TP .B "item" to make more clear which lines + are part of the list of commands and which aren't. -- libtest/Makefile.inc: remove superfluous variables + Closes #11371 - Rely on the defaults when possible. +Paul Wise (22 Jun 2023) - Closes #10765 +- checksrc: modernise perl file open -- tests/http: remove year ranges from copyrights + Use regular variables and separate file open modes from filenames. - Closes #10763 + Suggested by perlcritic -Casey Bodley (14 Mar 2023) + Copied from https://github.com/curl/trurl/commit/f2784a9240f47ee28a845 -- aws_sigv4: fall back to UNSIGNED-PAYLOAD for sign_as_s3 + Closes #11358 - all s3 requests default to UNSIGNED-PAYLOAD and add the required - x-amz-content-sha256 header. this allows CURLAUTH_AWS_SIGV4 to correctly - sign s3 requests to amazon with no additional configuration +Dan Fandrich (21 Jun 2023) - Signed-off-by: Casey Bodley +- runtests: work around a perl without SIGUSR1 - Closes #9995 + At least msys2 perl v5.32.1 doesn't seem to define this signal. Since + this signal is only used for debugging, just ignore if setting it fails. -Viktor Szakats (14 Mar 2023) + Reported-by: Marcel Raad + Fixes #11350 + Closes #11366 -- wolfssl: add quic/ngtcp2 detection in cmake, and fix builds +- runtests: include missing valgrind package - - add QUIC/ngtcp2 detection in CMake with wolfSSL. + use valgrind was missing which caused torture tests with valgrind + enabled to fail. - Because wolfSSL uses zlib if available, move compression detection - before TLS detection. (OpenSSL might also need this in the future.) + Reported-by: Daniel Stenberg + Fixes #11364 + Closes #11365 - - wolfSSL 5.5.0 started using C99 types in its `quic.h` header, but it - doesn't #include the necessary C99 header itself, breaking builds - (unless another dependency pulled it by chance.) Add local workaround - for it. For this to work with all build tools, we had to fix our - header detection first. Ref: #10745 +- runtests: use more consistent failure lines - Ref: https://github.com/curl/curl-for-win/commit/6ad5f6ecc15620c15625fc4434 - 76b3a1ecef4f3f + After a test failure log a consistent log message to make it easier to + parse the log file. Also, log a consistent message with "ignored" for + failures that cause the test to be not considered at all. These should + perhaps be counted in the skipped category, but this commit does not + change that behaviour. - Closes #10739 +- runtests: consistently write the test check summary block -Stefan Eissing (14 Mar 2023) + The memory check character was erroneously omitted if the memory + checking file was not available for some reason, making the block of + characters an inconsistent length. -- secure-transport: fix recv return code handling +- test2600: fix the description - Return code handling of recv calls were not always correct when an error - occured or the connection was closed. + It looks like it was cut-and-pasted. - Closes #10717 + Closes #11354 -- http2: Use KEEP_SEND_HOLD for flow control in HTTP/2 +Daniel Stenberg (21 Jun 2023) - - use the defined, but so far not used, KEEP_SEND_HOLD bit for flow - control based suspend of sending in transfers. +- TODO: "Support HTTP/2 for HTTP(S) proxies" *done* - Prior to this change KEEP_SEND_PAUSE bit was used instead, but that can - interfere with pausing streams from the user side via curl_easy_pause. +humbleacolyte (21 Jun 2023) - Fixes https://github.com/curl/curl/issues/10751 - Closes https://github.com/curl/curl/pull/10753 +- cf-socket: move ctx declaration under HAVE_GETPEERNAME -Dan Fandrich (13 Mar 2023) + Closes #11352 -- tests: fix control code that hid some text in runtests.1 +Daniel Stenberg (20 Jun 2023) -- tests: sync option lists in runtests.pl & its man page +- RELEASE-NOTES: synced -Daniel Stenberg (13 Mar 2023) +- example/connect-to: show CURLOPT_CONNECT_TO -- multi: make multi_perform ignore/unignore signals less often + Closes #11340 - For improved performance +Stefan Eissing (20 Jun 2023) - Reported-by: Jerome St-Louis - Ref: #10743 - Closes #10750 +- hyper: unslow -Viktor Szakats (13 Mar 2023) + - refs #11203 where hyper was reported as being slow + - fixes hyper_executor_poll to loop until it is out of + tasks as advised by @seanmonstar in https://github.com/hyperium/hyper/issue + s/3237 + - added a fix in hyper io handling for detecting EAGAIN + - added some debug logs to see IO results + - pytest http/1.1 test cases pass + - pytest h2 test cases fail on connection reuse. HTTP/2 + connection reuse does not seem to work. Hyper submits + a request on a reused connection, curl's IO works and + thereafter hyper declares `Hyper: [1] operation was canceled: connection cl + osed` + on stderr without any error being logged before. -- cmake: delete unused HAVE__STRTOI64 + Fixes #11203 + Reported-by: Gisle Vanem + Advised-by: Sean McArthur + Closes #11344 - Also delete obsolete surrounding comments. +- HTTP/2: upload handling fixes - Reviewed-by: Daniel Stenberg - Closes #10756 + - fixes #11242 where 100% CPU on uploads was reported + - fixes possible stalls on last part of a request body when + that information could not be fully send on the connection + due to an EAGAIN + - applies the same EGAIN handling to HTTP/2 proxying -- CI: fix copyright header + Reported-by: Sergey Alirzaev + Fixed #11242 + Closes #11342 - Follow-up to 395b9175b7422d699fa93643973295c106cdf147 +Daniel Stenberg (20 Jun 2023) -Daniel Stenberg (13 Mar 2023) +- example/opensslthreadlock: remove -- RELEASE-PROCEDURE.md: update coming release dates + This shows how to setup OpenSSL mutex callbacks, but this is not + necessary since OpenSSL 1.1.0 - meaning that no currently supported + OpenSSL version requires this anymore -Stefan Eissing (13 Mar 2023) + Closes #11341 -- tests/http: add pytest to GHA and improve tests +Dan Fandrich (19 Jun 2023) - - added to: ngtcp2-quictls, ngtcp2-gnutls and the linux varians - quiche, bearssl, libressl, mbedtls, openssl3, rustls - - added disabled in ngtcp2-wolfssl due to weird SSL_connect() errors - not reproducable locally +- libtest: display the times after a test timeout error - Improvements on pytest: + This is to help with test failure debugging. - - handling of systems with nghttpx in $PATH - - configure will seach $PATH got nghttpx used in pytest - - pytest fixes for managing nghttpx without h3 support - - ngtcp2-wolfssl: use a fully enabled wolfssl build + Ref: #11328 + Closes #11329 - - lower parallel count for http/1.1 tests, since we do not - want to test excessive connections. - - check built curl for HTTPS-proxy support in proxy tests - - bearssl does not like one of our critical cert extensions, making - it non-critical now - - bearssl is too slow for test_12, skipping - - making sure we do h3 tests only when curl and server support is there +- test2600: bump a test timeout - Closes #10699 - -Marcel Raad (13 Mar 2023) + Case 1 failed at least once on GHA by going 30 msec too long. -- tool_operate: silence unused parameter warning + Ref: #11328 - `global` is only used in the `my_setopt` macro version without - `CURL_DISABLE_LIBCURL_OPTION` since commit 4774decf10a. +- runtests: better detect and handle pipe errors in the controller - Closes https://github.com/curl/curl/pull/10752 + Errors reading and writing to the pipes are now better detected and + propagated up to the main test loop so it can be cleanly shut down. Such + errors are usually due to a runner dying so it doesn't make much sense + to try to continue the test run. -Viktor Szakats (13 Mar 2023) +- runtests: cleanly abort the runner if the controller dies -- build: fix stdint/inttypes detection with non-autotools + If the controller dies unexpectedly, have the runner stop its servers + and exit cleanly. Otherwise, the orphaned servers will stay running in + the background. - Fix `stdint.h` and `inttypes.h` detection with non-autotools builds on - Windows. (autotools already auto-detected them accurately.) +- runtests: improve error logging - `lib/config-win32.h` builds (e.g. `Makefile.mk`): - - set `HAVE_STDINT_H` where supported. - - set `HAVE_INTTYPES_H` for MinGW. + Give more information about test harness error conditions to help figure + out what might be wrong. Print some internal test state when SIGUSR1 is + sent to runtests.pl. - CMake: - - auto-detect them on Windows. (They were both force-disabled.) - - delete unused `CURL_PULL_STDINT_H`. - - delete unused `CURL_PULL_INTTYPES_H`. - - stop detecting `HAVE_STDINT_H` twice. - Present since the initial CMake commit: 4c5307b45655ba75ab066564afdc0c111a8 - b9291 + Ref: #11328 - curl doesn't use these C99 headers, we need them now to workaround - broken wolfSSL builds. Ref: #10739 +- runtests: better handle ^C during slow tests - Once that clears up, we can delete these detections and macros (unless - we want to keep them for future us.) + Since the SIGINT handler now just sets a flag that must be checked in the + main controller loop, make sure that runs periodically. Rather than + blocking on a response from a test runner near the end of the test run, + add a short timeout to allow it. - Reviewed-by: Daniel Stenberg - Closes #10745 +- runtests: rename server command file -Daniel Stenberg (13 Mar 2023) + The name ftpserver.cmd was historical and has been used for more than + ftp for many years now. Rename it to plain server.cmd to reduce + confusion. -- RELEASE-NOTES: synced +- tests: improve reliability of TFTP tests -- ftp: add more conditions for connection reuse + Stop checking the timeout used by the client under test (for most + tests). The timeout will change if the TFTP test server is slow (such as + happens on an overprovisioned CI server) because the client will retry + and reduce its timeout, and the actual value is not important for most + tests. - Reported-by: Harry Sintonen - Closes #10730 + test285 is changed a different way, by increasing the connect timeout. + This improves test coverage by allowing the changed timeout value to be + checked, but improves reliability with a carefully-chosen timeout that + not only allows twice the time to respond as before, but also allows + several retries before the client will change its timeout value. -Dan Fandrich (12 Mar 2023) + Ref: #11328 -- tests: make first.c the same for both lib tests and unit tests +Daniel Stenberg (19 Jun 2023) - The only difference used to be global variable used in unittest tests. - After cb7ed5a removed individual flag overrides for the unittests, first.c - was no longer recompiled for unit tests to include the flag, so whether it - worked or gave a link error depended on whether it was compiled in - libtest or unittest first. This way also speeds up the build by - eliminating 40 identical compile invocations. +- cf-socket: skip getpeername()/getsockname for TFTP - Fixes #10749 + Since the socket is not connected then the call fails. When the call + fails, failf() is called to write an error message that is then + surviving and is returned when the *real* error occurs later. The + earlier, incorrect, error therefore hides the actual error message. -- tests: use AM_CPPFILES to modify flags in unit tests + This could be seen in stderr for test 1007 - Using CPPFLAGS sometimes caused odd compile issues when building tests - with parallel make and AM_CPPFILES is the right flag, anyway. + Test 1007 has now been extended to verify the stderr message. - Follow-up to cb7ed5a + Closes #11332 - Ref #10749 +- example/crawler: make it use a few more options -Viktor Szakats (13 Mar 2023) + For show, but reasonable -- Makefile.mk: fix -g option in debug mode [ci skip] +- libcurl-ws.3: mention raw mode - Add it to `CFLAGS` (was: `LDFLAGS`). + Closes #11339 - Closes #10747 +- example/default-scheme: set the default scheme for schemeless URLs -Jay Satiro (12 Mar 2023) + Closes #11338 -- tool: improve --stderr handling +- example/hsts-preload: show one way to HSTS preload - - freopen stderr with the user-specified file (--stderr file) instead of - using a separate 'errors' stream. + Closes #11337 - - In tool_setup.h override stdio.h's stderr macro as global variable - tool_stderr. +- examples/http-options: show how to send "OPTIONS *" - Both freopen and overriding the stderr macro are necessary because if - the user-specified filename is "-" then stdout is assigned to - tool_stderr and no freopen takes place. See the PR for more information. + With CURLOPT_REQUEST_TARGET. - Ref: https://github.com/curl/curl/issues/10491 + Also add use of CURLOPT_QUICK_EXIT to show. - Closes https://github.com/curl/curl/pull/10673 + Closes #11333 -Dan Fandrich (11 Mar 2023) +- examples: make use of CURLOPT_(REDIR_|)PROTOCOLS_STR -- CI: don't run CI jobs if only another CI was changed + To show how to use them - Also skip builds on non-Windows platforms when only Windows build files - have changed. + Closes #11334 - This should reduce the number of useless builds and the associated - waiting time and chance of spurious failures, freeing resources for - new PRs. +- examples/smtp-mime: use CURLOPT_MAIL_RCPT_ALLOWFAILS - Closes #10742 + For show -- http: don't send 100-continue for short PUT requests + Closes #11335 - This is already how curl is documented to behave in Everything curl, but - in actuality only short POSTs skip this. This should knock 30 seconds - off a full run of the test suite since the 100-continue timeout will no - longer be hit. +- http: rectify the outgoing Cookie: header field size check - Closes #10740 + Previously it would count the size of the entire outgoing request and + not just the size of only the Cookie: header field - which was the + intention. -- tests: add DELAY keyword to more tests using waits + This could make the check be off by several hundred bytes in some cases. -- tests: hack to build most unit tests under cmake + Closes #11331 - These are only built when a libcurl static library is available, since - we're not building a special libcurlu library yet and these tests rely - on private symbols that aren't available in the shared library. A few - unit tests do require libcurlu, so those are not built. +Jay Satiro (17 Jun 2023) - Closes #10722 +- lib: fix some format specifiers -- tests: fix MSVC unreachable code warnings in unit tests + - Use CURL_FORMAT_CURL_OFF_T where %zd was erroneously used for some + curl_off_t variables. - Switch unit1654 to use the proper test macros as well. + - Use %zu where %zd was erroneously used for some size_t variables. -- tests: make CPPFLAGS common to all unit tests + Prior to this change some of the Windows CI tests were failing because + in Windows 32-bit targets have a 32-bit size_t and a 64-bit curl_off_t. + When %zd was used for some curl_off_t variables then only the lower + 32-bits was read and the upper 32-bits would be read for part or all of + the next specifier. - There's no need to specify them individually. + Fixes https://github.com/curl/curl/issues/11327 + Closes https://github.com/curl/curl/pull/11321 -- tests: keep cmake unit tests names in sync +Marcel Raad (16 Jun 2023) - Put only the test names into Makefile.inc so they can be used by both - cmake and automake. This will prevent the list of tests from becoming - out of date when they are also built under cmake. +- test427: add `cookies` feature and keyword -Viktor Szakats (11 Mar 2023) + This test doesn't work with `--disable-cookies`. -- src: silence wmain() warning for all build methods + Closes https://github.com/curl/curl/pull/11320 - llvm/clang and gcc doesn't recognize the wmain() function in Unicode - Windows builds: +Chris Talbot (15 Jun 2023) - llvm/clang: - ``` - ../../src/tool_main.c:239:5: warning: no previous prototype for function 'wma - in' [-Wmissing-prototypes] - int wmain(int argc, wchar_t *argv[]) - ^ - 1 warning generated. - ``` +- imap: Provide method to disable SASL if it is advertised - gcc: - ``` - ../../src/tool_main.c:239:5: warning: no previous prototype for 'wmain' [-Wmi - ssing-prototypes] - 239 | int wmain(int argc, wchar_t *argv[]) - | ^~~~~ - ``` + - Implement AUTH=+LOGIN for CURLOPT_LOGIN_OPTIONS to prefer plaintext + LOGIN over SASL auth. - Before this patch, we already silenced it with CMake. This patch moves - the silencing to the source, so that it applies to all build tools. + Prior to this change there was no method to be able to fall back to + LOGIN if an IMAP server advertises SASL capabilities. However, this may + be desirable for e.g. a misconfigured server. - Bug: https://github.com/curl/curl/issues/7229#issuecomment-1464806651 + Per: https://www.ietf.org/rfc/rfc5092.html#section-3.2 - Reviewed-by: Marcel Raad - Closes #10744 + ";AUTH=" looks to be the correct way to specify what + authenication method to use, regardless of SASL or not. -Dan Fandrich (10 Mar 2023) + Closes https://github.com/curl/curl/pull/10041 -- CI: fix retrying on brew failures +Daniel Stenberg (15 Jun 2023) - The previous attempt didn't consider that the shell would exit - immediately after the false statement in the retry case. +- RELEASE-NOTES: synced - Follow-up to dc141a37 +- examples/multi-debugcallback.c: avoid the bool typedef -Stefan Eissing (10 Mar 2023) + Apparently this cannot be done in c23 -- http2: fix error handling during parallel operations + Reported-by: Cristian Rodríguez + Fixes #11299 + Closes #11319 - RST and connection close were not handled correctly during parallel - transfers, leading to aborted response bodies being reported complete. +- docs/libcurl/libcurl.3: cleanups and improvements - Closes #10715 + Closes #11317 -Daniel Stenberg (10 Mar 2023) +- libcurl-ws.3: fix typo -- url: only reuse connections with same GSS delegation +- curl_ws_*.3: enhance - Reported-by: Harry Sintonen - Closes #10731 + - all: SEE ALSO the libcurl-ws man page + - send: add example and return value information + - meta: mention that the returned data is read-only -Viktor Szakats (10 Mar 2023) + Closes #11318 -- lib: silence clang/gcc -Wvla warnings in brotli headers +- docs/libcurl/libcurl-ws.3: see also CURLOPT_WS_OPTIONS - brotli v1.0.0 throughout current latest v1.0.9 and latest master [1] - trigger this warning. +- docs/libcurl/libcurl-ws.3: minor polish - It happened with CMake and GNU Make. autotools builds avoid it with - the `convert -I options to -isystem` macro. +- libcurl-ws.3. WebSocket API overview - llvm/clang: - ``` - In file included from ./curl/lib/content_encoding.c:36: - ./brotli/x64-ucrt/usr/include/brotli/decode.h:204:34: warning: variable lengt - h array used [-Wvla] - const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ./brotli/x64-ucrt/usr/include/brotli/port.h:253:34: note: expanded from macro - 'BROTLI_ARRAY_PARAM' - ^~~~~~ - In file included from ./curl/lib/content_encoding.c:36: - ./brotli/x64-ucrt/usr/include/brotli/decode.h:206:48: warning: variable lengt - h array used [-Wvla] - uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); - ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~ - ./brotli/x64-ucrt/usr/include/brotli/port.h:253:35: note: expanded from macro - 'BROTLI_ARRAY_PARAM' - ~^~~~~ - ``` + Closes #11314 - gcc: - ``` - In file included from ./curl/lib/content_encoding.c:36: - ./brotli/x64-ucrt/usr/include/brotli/decode.h:204:5: warning: ISO C90 forbids - variable length array 'encoded_buffer' [-Wvla] - 204 | const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], - | ^~~~~ - ./brotli/x64-ucrt/usr/include/brotli/decode.h:206:5: warning: ISO C90 forbids - variable length array 'decoded_buffer' [-Wvla] - 206 | uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); - | ^~~~~~~ - ``` +- libcurl-url.3: also mention CURLUPART_ZONEID - [1] https://github.com/google/brotli/commit/ed1995b6bda19244070ab5d331111f16f - 67c8054 + ... and sort the two part-using lists alphabetically - Reviewed-by: Daniel Stenberg - Reviewed-by: Marcel Raad - Closes #10738 +Marcel Raad (14 Jun 2023) -Daniel Stenberg (10 Mar 2023) +- fopen: fix conversion warning on 32-bit Android -- curl_path: create the new path with dynbuf + When building for 32-bit ARM or x86 Android, `st_mode` is defined as + `unsigned int` instead of `mode_t`, resulting in a + -Wimplicit-int-conversion clang warning because `mode_t` is + `unsigned short`. Add a cast to silence the warning. - Closes #10729 + Ref: https://android.googlesource.com/platform/bionic/+/refs/tags/ndk-r25c/li + bc/include/sys/stat.h#86 + Closes https://github.com/curl/curl/pull/11313 -- url: remove dummy protocol handler +- http2: fix variable type - Just two added checks were needed saves a whole handler struct. + `max_recv_speed` is `curl_off_t`, so using `size_t` might result in + -Wconversion GCC warnings for 32-bit `size_t`. Visible in the NetBSD + ARM autobuilds. - Closes #10727 + Closes https://github.com/curl/curl/pull/11312 -Dan Fandrich (10 Mar 2023) +Daniel Stenberg (13 Jun 2023) -- CI: retry a failed brew update too, not just brew install +- vtls: fix potentially uninitialized local variable warnings - Also, make sure an eventual failure ends up returning a failure code so - the job stops. + Follow-up from a4a5e438ae533c -Daniel Stenberg (10 Mar 2023) + Closes #11310 -- url: fix the SSH connection reuse check +- timeval: use CLOCK_MONOTONIC_RAW if available Reported-by: Harry Sintonen - Closes #10735 + Ref: #11288 + Closes #11291 -- CURLOPT_PROXY.3: curl+NSS does not handle HTTPS over unix domain socket +Stefan Eissing (12 Jun 2023) - It results in error "NSS error -5985 (PR_ADDRESS_NOT_SUPPORTED_ERROR)" +- tool: add curl command line option `--trace-ids` - Disabled test 1470 for NSS builds and documented the restriction. + - added and documented --trace-ids to prepend (after the timestamp) + the transfer and connection identifiers to each verbose log line + - format is [n-m] with `n` being the transfer id and `m` being the + connection id. In case there is not valid connection id, print 'x'. + - Log calls with a handle that has no transfer id yet, are written + without any ids. - Reported-by: Dan Fandrich - Fixes #10723 - Closes #10734 + Closes #11185 -- CURLSHOPT_SHARE.3: HSTS sharing is not thread-safe +- lib: add CURLINFO_CONN_ID and CURLINFO_XFER_ID - Reported-by: Hiroki Kurosawa - Closes #10732 + - add an `id` long to Curl_easy, -1 on init + - once added to a multi (or its own multi), it gets + a non-negative number assigned by the connection cache + - `id` is unique among all transfers using the same + cache until reaching LONG_MAX where it will wrap + around. So, not unique eternally. + - CURLINFO_CONN_ID returns the connection id attached to + data or, if none present, data->state.lastconnect_id + - variables and type declared in tool for write out -- telnet: only accept option arguments in ascii + Closes #11185 - To avoid embedded telnet negotiation commands etc. +Daniel Stenberg (12 Jun 2023) - Reported-by: Harry Sintonen - Closes #10728 +- CURLOPT_INFILESIZE.3: mention -1 triggers chunked -- test1903: test use of COOKIEFILE - reset - COOKIEFILE + Ref: #11300 + Closes #11304 - This also tests for the memory leak bug fixed by parent commit b559ef6f. +Philip Heiduck (12 Jun 2023) - Ref: #10694 +- CI: openssl-3.0.9+quic - Closes https://github.com/curl/curl/pull/10712 + Closes #11296 -Jay Satiro (10 Mar 2023) +Karthikdasari0423 (12 Jun 2023) -- url: fix cookielist memleak when curl_easy_reset +- HTTP3.md: update openssl version - - Free set.cookelist in Curl_freeset instead of Curl_close. + Closes #11297 - Prior to this change the cookielist linked list wasn't freed by - curl_easy_reset which calls Curl_freeset to free all set. +Daniel Stenberg (12 Jun 2023) - Bug: https://github.com/curl/curl/issues/10694#issuecomment-1458619157 - Reported-by: Sergey Ryabinin +- vtls: avoid memory leak if sha256 call fails - Closes https://github.com/curl/curl/pull/10709 + ... in the pinned public key handling function. -Dan Fandrich (10 Mar 2023) + Reported-by: lizhuang0630 on github + Fixes #11306 + Closes #11307 -- tests: fix some keywords and unused sections +- examples/ipv6: disable on win32 -- tests: fix test1301 to call the right binary + I can't make if_nametoindex() work there - It was refactored in commit 480ac6e5 but this step was missed. + Follow-up to c23dc42f3997acf23 -- tests: add timeout, SLOWDOWN and DELAY keywords to tests + Closes #11305 - These are tests that are testing timing and end up being quite slow. +- tool_operate: allow cookie lines up to 8200 bytes -Daniel Stenberg (10 Mar 2023) + Since this option might set multiple cookies in the same line, it does + not make total sense to cap this at 4096 bytes, which is the limit for a + single cookie name or value. -- RELEASE-NOTES: synced + Closes #11303 -Stefan Eissing (10 Mar 2023) +- test427: verify sending more cookies than fit in a 8190 bytes line -- wolfSSL: ressurect the BIO `io_result` + curl will then only populate the header with cookies that fit, dropping + ones that otherwise would have been sent - In pytest'ing the situation occored that wolfSSL reported an - IO error when the underlying BIO operation was returning an - CURLE_AGAIN condition. + Ref: https://curl.se/mail/lib-2023-06/0020.html - Readding the `io_result` filter context member to detect such - situations. + Closes #11303 - Also, making sure that the returned CURLcode is initialized - on all recv operations outcome. +- testutil: allow multiple %-operators on the same line - Closes #10716 + Closes #11303 -- gssapi: align global `gss_OID_desc` vars to silence ld warnings on macOS vent - ura +Oleg Jukovec (12 Jun 2023) - Refs #9975 which first reported this. +- docs: update CURLOPT_UPLOAD.3 - Closes #10718 + The behavior of CURLOPT_UPLOAD differs from what is described in the + documentation. The option automatically adds the 'Transfer-Encoding: + chunked' header if the upload size is unknown. -Daniel Stenberg (10 Mar 2023) + Closes #11300 -- libssh2: only set the memory callbacks when debugging +Daniel Stenberg (12 Jun 2023) - This makes us debug libssh2 less and libcurl more when for example - running torture tests that otherwise will spend a lot of time in libssh2 - functions. +- RELEASE-NOTES: synced - We leave libssh2 to test libssh2. +- CURLOPT_AWS_SIGV4.3: remove unused variable from example - Closes #10721 + Closes #11302 -- docs/SECURITY-PROCESS.md: updates +- examples/https.c: use CURLOPT_CA_CACHE_TIMEOUT - - allow Low+Medium issues to be managed through plain PRs - - update the bug-bounty part to reflect current reality + for demonstration purposes - Closes #10719 + Closes #11290 -Dan Fandrich (9 Mar 2023) +- example/ipv6: feature CURLOPT_ADDRESS_SCOPE in use -- tests: fix tag markup issues in some tests + Closes #11282 -Marcel Raad (9 Mar 2023) +Karthikdasari0423 (10 Jun 2023) -- tests: add `cookies` features +- docs: Update HTTP3.md for newer ngtcp2 and nghttp3 - These tests don't work with `--disable-cookies`. + Follow-up to fb9b9b58 - Closes https://github.com/curl/curl/pull/10713 + Ref: #11184 + Closes #11295 -- test420: add cookies keyword +Dan Fandrich (10 Jun 2023) - It fails with `--disable-cookies`. +- docs: update the supported ngtcp2 and nghttp3 versions - Closes https://github.com/curl/curl/pull/10713 + Follow-up to cae9d10b -Dan Fandrich (8 Mar 2023) - -- CI: Add more labeler match patterns - - Also, add the CI, tests or libcurl API tags in conjunction with any - others that might also apply. - -Andy Alt (9 Mar 2023) + Ref: #11184 + Closes #11294 -- GHA: minor improvements to spellcheck +- tests: fix error messages & handling around sockets - Closes #10640 + The wrong error code was checked on Windows on UNIX socket failures, + which could have caused all UNIX sockets to be reported as having + errored and the tests therefore skipped. Also, a useless error message + was displayed on socket errors in many test servers on Windows because + strerror() doesn't work on WinSock error codes; perror() is overridden + there to work on all errors and is used instead. -Daniel Stenberg (9 Mar 2023) + Ref #11258 + Closes #11265 -- test1671: fix after fix +Daniel Stenberg (9 Jun 2023) -- test421: -w %{header_json} test with multiple same header names +- CURLOPT_SSH_PRIVATE_KEYFILE.3: expand on the file search - To reproduce the issue in #10704 + Reported-by: atjg on github + Ref: #11287 + Closes #11289 -- tool_writeout_json. fix the output for duplicate header names +Stefan Eissing (9 Jun 2023) - Header entries with index != 0 are handled at the index 0 level so they - should then be skipped when iterated over. +- ngtcp2: use ever increasing timestamp in io - Reported-by: Boris Okunskiy - Fixes #10704 - Closes #10707 + - ngtcp2 v0.16.0 asserts that timestamps passed to its function + will only ever increase. + - Use a context shared between ingress/egress operations that + uses a shared timestamp, regularly updated during calls. -- headers: make curl_easy_header and nextheader return different buffers + Closes #11288 - By letting curl_easy_header() and curl_easy_nextheader() store the - header data in their own struct storage when they return a pointer to - it, it makes it possible for applications to use them both in a loop. - Like the curl tool does. +Daniel Stenberg (9 Jun 2023) - Reported-by: Boris Okunskiy - Fixes #10704 - Closes #10707 +- GHA: use nghttp2 1.54.0 for the ngtcp2 jobs -rcombs (8 Mar 2023) +Philip Heiduck (9 Jun 2023) -- urlapi: take const args in _dup and _get functions +- GHA: ngtcp2: use 0.16.0 and nghttp3 0.12.0 - Closes #10708 +Daniel Stenberg (9 Jun 2023) -- urlapi: avoid mutating internals in getter routine +- ngtcp2: build with 0.16.0 and nghttp3 0.12.0 - This was not intended. + - moved to qlog_write + - crypto => encryption + - CRYPTO => ENCRYPTION + - removed "_is_" + - ngtcp2_conn_shutdown_stream_read and + ngtcp2_conn_shutdown_stream_write got flag arguments + - the nghttp3_callbacks struct got a recv_settings callback - Closes #10708 + Closes #11184 -Daniel Stenberg (8 Mar 2023) +- example/http2-download: set CURLOPT_BUFFERSIZE -- urlapi: '%' is illegal in host names + Primarily because no other example sets it, and remove the disabling of + the certificate check because we should not recommend that. - Update test 1560 to verify + Closes #11284 - Ref: #10708 - Closes #10711 +- example/crawler: also set CURLOPT_AUTOREFERER -- ftp: make the 'ftpauth' a more normal 'char *'-array + Could make sense, and it was not used in any example before. - Closes #10703 + Closes #11283 -Evgeny Grin (Karlson2k) (8 Mar 2023) +Wyatt OʼDay (9 Jun 2023) -- doc: fix compiler warning in libcurl.m4 +- tls13-ciphers.d: include Schannel - Current test for curl_free() may produce warnings with strict compiler - flags or even with default compiler flags with upcoming versions. - These warning could turned into errors by -Werror or similar flags. - Such warnings/errors are avoided by this patch. + Closes #11271 - Closes #10710 +Daniel Stenberg (9 Jun 2023) -Viktor Szakats (8 Mar 2023) +- curl_pushheader_byname/bynum.3: document in their own man pages -- misc: fix typos + These two functions were added in 7.44.0 when CURLMOPT_PUSHFUNCTION was + introduced but always lived a life in the shadows, embedded in the + CURLMOPT_PUSHFUNCTION man page. Until now. - Closes #10706 + It makes better sense and gives more visibility to document them in + their own stand-alone man pages. -Stefan Eissing (7 Mar 2023) + Closes #11286 -- ftp: active mode with SSL, add the damn filter +- curl_mprintf.3: minor fix of the example - - since 7.87.0 we lost adding the SSL filter for an active - FTP connection that uses SSL. This leads to hangers and timeouts - as reported in #10666. +- curl_url_set: enforce the max string length check for all parts - Reported-by: SandakovMM on github - Fixes #10666 - Closes #10669 + Update the docs and test 1559 accordingly -Daniel Stenberg (7 Mar 2023) + Closes #11273 -- docs: extend the URL API descriptions +- examples/ftpuploadresume.c: add use of CURLOPT_ACCEPTTIMEOUT_MS - Closes #10701 + For show -Stefan Eissing (7 Mar 2023) + Closes #11277 -- url: fix logic in connection reuse to deny reuse on "unclean" connections +- examples/unixsocket.c: example using CURLOPT_UNIX_SOCKET_PATH - - add parameter to `conn_is_alive()` cfilter method that returns - if there is input data waiting on the connection - - refrain from re-using connnection from the cache that have - input pending - - adapt http/2 and http/3 alive checks to digest pending input - to check the connection state - - remove check_cxn method from openssl as that was just doing - what the socket filter now does. - - add tests for connection reuse with special server configs + and alternatively CURLOPT_ABSTRACT_UNIX_SOCKET - Closes #10690 + Closes #11276 -Daniel Stenberg (6 Mar 2023) +Anssi Kolehmainen (8 Jun 2023) -- x509asn1: use plain %x, not %lx, when the arg is an int +- docs: fix missing parameter names in examples - Pointed out by Coverity. + Closes #11278 - Closes #10689 +Daniel Stenberg (8 Jun 2023) -Stefan Eissing (6 Mar 2023) +- urlapi: have *set(PATH) prepend a slash if one is missing -- http2: fix handling of RST and GOAWAY to recognize partial transfers + Previously the code would just do that for the path when extracting the + full URL, which made a subsequent curl_url_get() of the path to + (unexpectedly) still return it without the leading path. - - a reset transfer (HTTP/2 RST) did not always lead to the proper - error message on receiving its response, leading to wrong reports - of a successful transfer - - test_05_02 was able to trigger this condition with increased transfer - count. The simulated response errors did not carry a 'Content-Length' - so only proper RST handling could detect the abort - - When doing such transfers in parallel, a connection could enter the - state where - a) it had been closed (GOAWAY received) - b) the RST had not been "seen" for the transfer yet - or c) the GOAWAY announced an error and the last successful - stream id was not checked against ongoing transfers + Amend lib1560 to verify this. Clarify the curl_url_set() docs about it. - Closes #10693 + Bug: https://curl.se/mail/lib-2023-06/0015.html + Closes #11272 + Reported-by: Pedro Henrique -- tests: use dynamic ports numbers in pytest suite +Dan Fandrich (7 Jun 2023) - - necessary ports are bound at start of test suite and then - given to server fixtures for use. - - this make parallel use of pytest (in separate directories), - practically safe for use as OS tend to not reuse such port numbers - for a while +- runtests; give each server a unique log lock file - Closes #10692 + Logs are written by several servers and all of them must be finished + writing before the test results can be determined. This means each + server must have its own lock file rather than sharing a single one, + which is how it was done up to now. Previously, the first server to + complete a test would clear the lock before the other server was done, + which caused flaky tests. -- connect: fix time_connect and time_appconnect timer statistics + Lock files are now all found in their own directory, so counting locks + equals counting the files in that directory. The result is that the + proxy logs are now reliably written which actually changes the expected + output for two tests. - - time_connect was not updated when the overall connection failed, - e.g. when SSL verification was unsuccessful, refs #10670 - - rework gather those values to interrogate involved filters, - also from all eyeballing attempts, to report the maximum of - those values. - - added 3 test cases in test_06 to check reported values on - successful, partially failed and totally failed connections. + Fixes #11231 + Closes #11259 - Reported-by: Master Inspire - Fixes #10670 - Closes #10671 +- runtests: make test file directories in log/N -Daniel Stenberg (6 Mar 2023) + Test files in subdirectories were not created after parallel test log + directories were moved down a level due to a now-bad comparison. -- test1905: update output cookie order + Follow-up to 92d7dd39 - After the #10685 update + Ref #11264 + Closes #11267 -- test420: verify expiring cookies +Daniel Stenberg (7 Jun 2023) - Cookies that are loaded fine from a jar but then are expired in headers. +- ws: make the curl_ws_meta() return pointer a const -- cookie: don't load cookies again when flushing + The returned info is read-only for the user. - Reported-by: Sergio Mijatovic - Fixes #10677 - Closes #10685 + Closes #11261 - RELEASE-NOTES: synced -Andy Alt (6 Mar 2023) +- runtests: move parallel log dirs from logN to log/N -- docs: note '--data-urlencode' option + Having several hundreds of them in there gets annoying. - Closes #10687 + Closes #11264 -Daniel Stenberg (6 Mar 2023) +Dan Fandrich (7 Jun 2023) -- DEPRECATE: the original legacy mingw version 1 +- test447: move the test file into %LOGDIR - Remove completely in September 2023 +Viktor Szakats (7 Jun 2023) - Closes #10667 +- cmake: add support for "unity" builds -Harry Sintonen (6 Mar 2023) + Aka "jumbo" or "amalgamation" builds. It means to compile all sources + per target as a single C source. This is experimental. -- rand: use arc4random as fallback when available + You can enable it by passing `-DCMAKE_UNITY_BUILD=ON` to cmake. + It requires CMake 3.16 or newer. - Normally curl uses cryptographically strong random provided by the - selected SSL backend. If compiled without SSL support, a naive built-in - function was used instead. + It makes builds (much) faster, allows for better optimizations and tends + to promote less ambiguous code. - Generally this was okay, but it will result in some downsides for non- - SSL builds, such as predictable temporary file names. + Also add a new AppVeyor CI job and convert an existing one to use + "unity" mode (one MSVC, one MinGW), and enable it for one macOS CI job. - This change ensures that arc4random will be used instead, if available. + Fix related issues: + - add missing include guard to `easy_lock.h`. + - rename static variables and functions (and a macro) with names reused + across sources, or shadowed by local variables. + - add an `#undef` after use. + - add a missing `#undef` before use. + - move internal definitions from `ftp.h` to `ftp.c`. + - `curl_memory.h` fixes to make it work when included repeatedly. + - stop building/linking curlx bits twice for a static-mode curl tool. + These caused doubly defined symbols in unity builds. + - silence missing extern declarations compiler warning for ` _CRT_glob`. + - fix extern declarations for `tool_freq` and `tool_isVistaOrGreater`. + - fix colliding static symbols in debug mode: `debugtime()` and + `statename`. + - rename `ssl_backend_data` structure to unique names for each + TLS-backend, along with the `ssl_connect_data` struct member + referencing them. This required adding casts for each access. + - add workaround for missing `[P]UNICODE_STRING` types in certain Windows + builds when compiling `lib/ldap.c`. To support "unity" builds, we had + to enable `SCHANNEL_USE_BLACKLISTS` for Schannel (a Windows + `schannel.h` option) _globally_. This caused an indirect inclusion of + Windows `schannel.h` from `ldap.c` via `winldap.h` to have it enabled + as well. This requires `[P]UNICODE_STRING` types, which is apperantly + not defined automatically (as seen with both MSVS and mingw-w64). + This patch includes `` to fix it. + Ref: https://github.com/curl/curl/runs/13987772013 + Ref: https://dev.azure.com/daniel0244/curl/_build/results?buildId=15827&vie + w=logs&jobId=2c9f582d-e278-56b6-4354-f38a4d851906&j=2c9f582d-e278-56b6-4354-f + 38a4d851906&t=90509b00-34fa-5a81-35d7-5ed9569d331c + - tweak unity builds to compile `lib/memdebug.c` separately in memory + trace builds to avoid PP confusion. + - force-disable unity for test programs. + - do not compile and link libcurl sources to libtests _twice_ when libcurl + is built in static mode. - Closes #10672 + KNOWN ISSUES: + - running tests with unity builds may fail in cases. + - some build configurations/env may not compile in unity mode. E.g.: + https://ci.appveyor.com/project/curlorg/curl/builds/47230972/job/51wfesgnfu + auwl8q#L250 -Grisha Levit (6 Mar 2023) + Ref: https://github.com/libssh2/libssh2/issues/1034 + Ref: https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html + Ref: https://en.wikipedia.org/wiki/Unity_build -- tool: dump headers even if file is write-only + Closes #11095 - The fixes in #10079 brought a (seemingly unrelated) change of open mode - from `wb`/`ab` to `wb+`/`ab+` for the headerfile. This makes it no - longer possible to write the header file to e.g. a pipe, like: +Daniel Stenberg (7 Jun 2023) - curl -D >(grep ...) file:///dev/null +- examples/websocket.c: websocket example using CONNECT_ONLY - Which presently results in `Warning: Failed to open /dev/fd/63` + Closes #11262 - See #10079 - Closes #10675 +- websocket-cb: example doing WebSocket download using callback -Jay Satiro (6 Mar 2023) + Very basic -- tests: fix gnutls-serv check + Closes #11260 - - If gnutls-serv doesn't exist then don't try to execute it. +- test/.gitignore: ignore log* - Follow-up to 2fdc1d81. +Dan Fandrich (5 Jun 2023) - Closes https://github.com/curl/curl/pull/10688 +- runtests: document the -j parallel testing option -Daniel Stenberg (6 Mar 2023) + Reported-by: Daniel Stenberg + Ref: #10818 + Closes #11255 -- lib1560: fix enumerated type mixed with another type +- runtests: create multiple test runners when requested - Follow-up to c84c0f9aa3bb006 + Parallel testing is enabled by using a nonzero value for the -j option + to runtests.pl. Performant values seem to be about 7*num CPU cores, or + 1.3*num CPU cores if Valgrind is in use. - Closes #10684 + Flaky tests due to improper log locking (bug #11231) are exacerbated + while parallel testing, so it is not enabled by default yet. -Viktor Szakats (5 Mar 2023) + Fixes #10818 + Closes #11246 -- cmake: fix enabling LDAPS on Windows +- runtests: handle repeating tests in multiprocess mode - Before this patch, enabling LDAPS required a manual C flag: - https://github.com/curl/curl-for-win/blob/c1cfc31cfc04f24f7a4f946564d6f0e1b4d - 7dd36/curl-cmake.sh#L105 + Such as what happens with the --repeat option. Some functions are + changed to pass the runner ID instead of relying on the non-unique test + number. - Fix this and enable LDAPS automatically when using `wldap32` (and - when not explicitly disabled). This matches autotools and `Makefile.mk` - behavior. Also remove issue from KNOWN_BUGS. + Ref: #10818 - Add workaround for MSVS 2010 warning triggered by LDAPS now enabled - in more CI tests: - `ldap.c(360): warning C4306: 'type cast' : conversion from 'int' to 'void *' - of greater size` - Ref: https://ci.appveyor.com/project/curlorg/curl/builds/46408284/job/v8mwl9y - fbmoeqwlr#L312 +- runtests: buffer logmsg while running singletest() - Reported-by: JackBoosY on github - Reviewed-by: Jay Satiro - Reviewed-by: Marcel Raad - Fixes #6284 - Closes #10674 + This allows all messages relating to a single test case to be displayed + together at the end of the test. -- Makefile.mk: delete redundant `HAVE_LDAP_SSL` macro [ci skip] + Ref: #10818 - Since abebb2b8939c6b3e0f951eb2d9ec3729b569aa2c, we set this macro for - all Windows `wldap32` builds using `Makefile.mk`. +- runtests: call initserverconfig() in the runner - For OpenLDAP builds this macro is not enough to enable LDAPS, and - OpenLDAP is not an option in `Makefile.mk`. For Novell LDAP it might - have helped, but it's also not an option anymore in `Makefile.mk`. + This must be done so variables pick up the runner's unique $LOGDIR. - The future for LDAPS is that we should enable it by default without - extra build knobs. + Ref: #10818 - Reviewed-by: Marcel Raad - Closes #10681 +- runtests: use a per-runner random seed -- cmake: skip CA-path/bundle auto-detection in cross-builds + Each runner needs a unique random seed to reduce the chance of port + number collisions. The new scheme uses a consistent per-runner source of + randomness which results in deterministic behaviour, as it did before. - Also remove issue from KNOWN_BUGS. + Ref: #10818 - Reported-by: Cristian Morales Vega - Reviewed-by: Marcel Raad - Fixes #6178 - Closes #10676 +- runtests: complete main test loop refactor for multiple runners -Daniel Stenberg (3 Mar 2023) + The main test loop is now able to handle multiple runners, or no + additional runner processes at all. At most one process is still + created, however. -- schannel: loop over the algos to pick the selected one + Ref: #10818 - Avoid using the funny macro and the extra buffer copy. +- runtests: prepare main test loop for multiple runners - Closes #10647 + Some variables are expanded to arrays and hashes so that multiple + runners can be used for running tests. -- wildcard: remove files and move functions into ftplistparser.c + Ref: #10818 -- ftp: allocate the wildcard struct on demand +Stefan Eissing (5 Jun 2023) - The feature is rarely used so this frees up data for the vast majority - of easy handles that don't use it. +- bufq: make write/pass methods more robust - Rename "protdata" to "ftpwc" since it is always an FTP wildcard struct - pointer. Made the state struct field an unsigned char to save space. + - related to #11242 where curl enters busy loop when + sending http2 data to the server - Closes #10639 + Closes #11247 -- lib1560: test parsing URLs with ridiculously large fields +Boris Verkhovskiy (5 Jun 2023) - In the order of 120K. +- tool_getparam: fix comment - Closes #10665 + Closes #11253 -Brad Spencer (3 Mar 2023) +Raito Bezarius (5 Jun 2023) -- urlapi: parse IPv6 literals without ENABLE_IPV6 +- haproxy: add --haproxy-clientip flag to spoof client IPs - This makes the URL parser API stable and working the same way - independently of libcurl supporting IPv6 transfers or not. + CURLOPT_HAPROXY_CLIENT_IP in the library - Closes #10660 + Closes #10779 -Jan Engelhardt (3 Mar 2023) +Daniel Stenberg (5 Jun 2023) -- build: drop the use of XC_AMEND_DISTCLEAN +- curl: add --ca-native and --proxy-ca-native - Because automake used to delete depdirs at once (.deps) and there was an issu - e - with portability, curl's XC_AMEND_DISTCLEAN greps the Makefiles in an attempt - to build a list of all depfiles and delete them individually instead. + These are two boolean options to ask curl to use the native OS's CA + store when verifying TLS servers. For peers and for proxies + respectively. - Since commit 08849db866b44510f6b8fd49e313c91a43a3dfd3, automake switched from - deleting directories to individual files. curl's custom logic now finds a lot - more results with the grep (the filtering of these results isn't great), whic - h - causes a massive bloating of the Makefile in the order of O(n^2). + They currently only have an effect for curl on Windows when built to use + OpenSSL for TLS. - Also remove now-unused XC_AMEND_DISTCLEAN macro group + Closes #11049 - References: https://github.com/curl/curl/issues/9843 - References: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=59288 +Viktor Szakats (5 Jun 2023) - Reported-by: Ilmari Lauhakangas - Fixes #9843 - Closes #10661 +- build: drop unused/redundant `HAVE_WINLDAP_H` -Balakrishnan Balasubramanian (3 Mar 2023) + Sources did not use it. Autotools used it when checking for the + `winldap` library, which is redundant. -- test1470: test socks proxy using unix sockets and connect to https + With CMake, detection was broken: + ``` + Run Build Command(s):/usr/local/Cellar/cmake/3.26.3/bin/cmake -E env VERBOSE= + 1 /usr/bin/make -f Makefile cmTC_2d8fe/fast && /Library/Developer/CommandLine + Tools/usr/bin/make -f CMakeFiles/cmTC_2d8fe.dir/build.make CMakeFiles/cmTC_2 + d8fe.dir/build + Building C object CMakeFiles/cmTC_2d8fe.dir/HAVE_WINLDAP_H.c.obj + /usr/local/opt/llvm/bin/clang --target=x86_64-w64-mingw32 --sysroot=/usr/loca + l/opt/mingw-w64/toolchain-x86_64 -D_WINSOCKAPI_="" -I/my/quictls/x64-ucrt/usr + /include -I/my/zlib/x64-ucrt/usr/include -I/my/brotli/x64-ucrt/usr/include -W + no-unused-command-line-argument -D_UCRT -DCURL_HIDDEN_SYMBOLS -DHAVE_SSL_SE + T0_WBIO -DHAS_ALPN -DNGHTTP2_STATICLIB -DNGHTTP3_STATICLIB -DNGTCP2_STATICLIB + -DUSE_MANUAL=1 -fuse-ld=lld -Wl,-s -static-libgcc -lucrt -Wextra -Wall -p + edantic -Wbad-function-cast -Wconversion -Winline -Wmissing-declarations -Wmi + ssing-prototypes -Wnested-externs -Wno-long-long -Wno-multichar -Wpointer-ari + th -Wshadow -Wsign-compare -Wundef -Wunused -Wwrite-strings -Wcast-align -Wde + claration-after-statement -Wempty-body -Wendif-labels -Wfloat-equal -Wignored + -qualifiers -Wno-format-nonliteral -Wno-sign-conversion -Wno-system-headers - + Wstrict-prototypes -Wtype-limits -Wvla -Wshift-sign-overflow -Wshorten-64-to- + 32 -Wdouble-promotion -Wenum-conversion -Wunused-const-variable -Wcomma -Wmis + sing-variable-declarations -Wassign-enum -Wextra-semi-stmt -MD -MT CMakeFile + s/cmTC_2d8fe.dir/HAVE_WINLDAP_H.c.obj -MF CMakeFiles/cmTC_2d8fe.dir/HAVE_WINL + DAP_H.c.obj.d -o CMakeFiles/cmTC_2d8fe.dir/HAVE_WINLDAP_H.c.obj -c /my/curl/b + ld-cmake-llvm-x64-shared/CMakeFiles/CMakeScratch/TryCompile-3JP6dR/HAVE_WINLD + AP_H.c + In file included from /my/curl/bld-cmake-llvm-x64-shared/CMakeFiles/CMakeScra + tch/TryCompile-3JP6dR/HAVE_WINLDAP_H.c:2: + In file included from /usr/local/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mi + ngw32/include/winldap.h:17: + In file included from /usr/local/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mi + ngw32/include/schnlsp.h:9: + In file included from /usr/local/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mi + ngw32/include/schannel.h:10: + /usr/local/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32/include/wincrypt + .h:5041:254: error: unknown type name 'PSYSTEMTIME' + WINIMPM PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate (HCRYPTPROV_OR_ + NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey, PCERT_NAME_BLOB pSubjectIssuerBlob, + DWORD dwFlags, PCRYPT_KEY_PROV_INFO pKeyProvInfo, PCRYPT_ALGORITHM_IDENTIFIER + pSignatureAlgorithm, PSYSTEMTIME pStartTime, PSYSTEMTIME pEndTime, PCERT_EXT + ENSIONS pExtensions); + + + + ^ + /usr/local/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32/include/wincrypt + .h:5041:278: error: unknown type name 'PSYSTEMTIME' + WINIMPM PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate (HCRYPTPROV_OR_ + NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey, PCERT_NAME_BLOB pSubjectIssuerBlob, + DWORD dwFlags, PCRYPT_KEY_PROV_INFO pKeyProvInfo, PCRYPT_ALGORITHM_IDENTIFIER + pSignatureAlgorithm, PSYSTEMTIME pStartTime, PSYSTEMTIME pEndTime, PCERT_EXT + ENSIONS pExtensions); + + + + ^ + 2 errors generated. + make[1]: *** [CMakeFiles/cmTC_2d8fe.dir/HAVE_WINLDAP_H.c.obj] Error 1 + make: *** [cmTC_2d8fe/fast] Error 2 + exitCode: 2 + ``` - Similar to test1468 except using https instead of http + Cherry-picked from #11095 88e4a21ff70ccef391cf99c8165281ff81374503 + Reviewed-by: Daniel Stenberg + Closes #11245 - Closes #10662 +Daniel Stenberg (5 Jun 2023) -Daniel Stenberg (3 Mar 2023) +- urlapi: scheme starts with alpha -- test1960: verify CURL_SOCKOPT_ALREADY_CONNECTED + Add multiple tests to lib1560 to verify - When returned from the CURLOPT_SOCKOPTFUNCTION, like when we have a - custom socket connected in the app, passed in to libcurl. + Fixes #11249 + Reported-by: ad0p on github + Closes #11250 - Verifies the fix in #10648 +- RELEASE-NOTES: synced - Closes #10651 +- CURLOPT_MAIL_RCPT_ALLOWFAILS: replace CURLOPT_MAIL_RCPT_ALLLOWFAILS -Stefan Eissing (2 Mar 2023) + Deprecate the name using three Ls and prefer the name with two. -- tests: rename tests/tests-httpd to tests/http + Replaces #10047 + Closes #11218 - - httpd is only one server we test with - - the suite coveres the HTTP protocol in general where - the default test cases need a more beefy environment +- tests/servers: generate temp names in /tmp for unix domain sockets - Closes #10654 + ... instead of putting them in the regular pid directories because + systems generally have strict length requirements for the path name to + be shorter than 107 bytes and we easily hit that boundary otherwise. -- socket: detect "dead" connections better, e.g. not fit for reuse + The new concept generates two random names: one for the socks daemon and + one for http. - - refs #10646 where reuse was attempted on closed connections in the - cache, leading to an exhaustion of retries on a transfer - - the mistake was that poll events like POLLHUP, POLLERR, etc - were regarded as "not dead". - - change cf-socket filter check to regard such events as inidication - of corpsiness. - - vtls filter checks: fixed interpretation of backend check result - when inconclusive to interrogate status further down the filter - chain. + Reported-by: Andy Fiddaman + Fixes #11152 + Closes #11166 - Reported-by: SendSonS on github - Fixes #10646 - Closes #10652 +Stefan Eissing (2 Jun 2023) -- lib: give source files cf-http.* better fitting names +- http2: better support for --limit-rate - Closes #10656 + - leave transfer loop when --limit-rate is in effect and has + been received + - adjust stream window size to --limit-rate plus some slack + to make the server observe the pacing we want + - add test case to confirm behaviour -- http2: fix code indent + Closes #11115 - Closes https://github.com/curl/curl/pull/10655 +- curl_log: evaluate log statement only when transfer is verbose -Shankar Jadhavar (1 Mar 2023) + Closes #11238 -- cf-socket: if socket is already connected, return CURLE_OK +Daniel Stenberg (2 Jun 2023) - In 7.87.0, if callback method for CURLOPT_SOCKOPTFUNCTION returns - CURL_SOCKOPT_ALREADY_CONNECTED then curl library used to return - CURLE_OK. n 7.88.0, now even if callback returns - CURL_SOCKOPT_ALREADY_CONNECTED, curl library still tries to connect to - socket by invoking method do_connect(). +- libssh2: provide error message when setting host key type fails - This is regression caused by commit - https://github.com/curl/curl/commit/71b7e0161032927cdfb + Ref: https://curl.se/mail/archive-2023-06/0001.html - Fix: Check if we are already connected and return CURLE_OK. + Closes #11240 - Fixes #10626 - Closes #10648 +Igor Todorovski (2 Jun 2023) -Jay Satiro (1 Mar 2023) +- system.h: remove __IBMC__/__IBMCPP__ guards and apply to all z/OS compiles -- DYNBUF.md: note Curl_dyn_add* calls Curl_dyn_free on failure + Closes #11241 - This is the existing behavior and it has been widely assumed in the - codebase. +Daniel Stenberg (2 Jun 2023) - Closes https://github.com/curl/curl/pull/10645 +- docs/SECURITY-PROCESS.md: link to example of previous critical flaw -Stefan Eissing (1 Mar 2023) +Mark Seuffert (2 Jun 2023) -- http2: fix upload busy loop +- README.md: updated link to opencollective - - Set KEEP_SEND_PAUSE when exhausting remote HTTP/2 window size of a - stream. + Closes #11232 - - Clear KEEP_SEND_PAUSE when receiving HTTP/2 window updates on a paused - stream. +Daniel Stenberg (1 Jun 2023) - - Also fix http2 send compiler warnings reported in #10449. +- libssh2: use custom memory functions - Prior to this change, starting in 71b7e016 which precedes 7.88.0, - libcurl may eat CPU during HTTP/2 upload. + Because of how libssh2_userauth_keyboard_interactive_ex() works: the + libcurl callback allocates memory that is later free()d by libssh2, we + must set the custom memory functions. - Reported-by: Jay Satiro + Reverts 8b5f100db388ee60118c08aa28 - Fixes https://github.com/curl/curl/issues/10449 - Fixes https://github.com/curl/curl/issues/10618 - Closes https://github.com/curl/curl/pull/10627 + Ref: https://github.com/libssh2/libssh2/issues/1078 + Closes #11235 -Daniel Stenberg (1 Mar 2023) +- test447: test PUTting a file that grows -- sectransp: make read_cert() use a dynbuf when loading + ... and have curl trim the end when it reaches the expected total amount + of bytes instead of over-sending. - Closes #10632 + Reported-by: JustAnotherArchivist on github + Closes #11223 -Jay Satiro (1 Mar 2023) +- curl: count uploaded data to stop at the originally given size -- transfer: limit Windows SO_SNDBUF updates to once a second + Closes #11223 + Fixes #11222 + Reported-by: JustAnotherArchivist on github - - Change readwrite_upload() to call win_update_buffer_size() no more - than once a second to update SO_SNDBUF (send buffer limit). +- tool: remove exclamation marks from error/warning messages - Prior to this change during an upload readwrite_upload() could call - win_update_buffer_size() anywhere from hundreds of times per second to - an extreme test case of 100k per second (which is likely due to a bug, - see #10618). In the latter case WPA profiler showed - win_update_buffer_size was the highest capture count in - readwrite_upload. In any case the calls were excessive and unnecessary. +- tool: use errorf() for error output - Ref: https://github.com/curl/curl/pull/2762 + Convert a number of fprintf() calls. - Closes https://github.com/curl/curl/pull/10611 +- tool: remove newlines from all helpf/notef/warnf/errorf calls -Daniel Stenberg (28 Feb 2023) + Make voutf() always add one. -- RELEASE-NOTES: synced + Closes #11226 -Stefan Eissing (28 Feb 2023) +- tests/servers.pm: pick unused port number with a server socket -- http2: fix for http2-prior-knowledge when reusing connections + This change replaces the previous method of picking a port number at + random to try to start servers on, then retrying up to ten times with + new random numbers each time, with a function that creates a server + socket on port zero, thereby getting a suitable random port set by the + kernel. That server socket is then closed and that port number is used + to setup the actual test server on. - - refs #10634 where errors in the HTTP/2 framing layer are observed. - - the bug was that on connection reuse, the code attempted to switch - in yet another layer of HTTP/2 handling instead of detecting that - this was already in place. - - added pytest testcase reproducing the issue. + There is a risk that *another* server can be started on the machine in + the time gap, but the server verification feature will detect that. - Reported-by: rwmjones on github - Fixes #10634 - Closes #10643 + Closes #11220 -- cf-socket: fix handling of remote addr for accepted tcp sockets +- RELEASE-NOTES: synced - - do not try to determine the remote address of a listen socket. There - is none. - - Update remote address of an accepted socket by getpeername() if - available. + bump to 8.2.0 - Reported-by: Harry Sintonen - Fixes #10622 - Closes #10642 +Alejandro R. Sedeño (31 May 2023) -- http: fix unix domain socket use in https connects +- configure: fix run-compiler for old /bin/sh - - when h2/h3 eyeballing was involved, unix domain socket - configurations were not honoured - - configuring --unix-socket will disable HTTP/3 as candidate for eyeballing - - combinatino of --unix-socket and --http3-only will fail during initialisati - on - - adding pytest test_11 to reproduce + If you try to assign and export on the same line on some older /bin/sh + implementations, it complains: - Reported-by: Jelle van der Waa - Fixes #10633 - Closes #10641 + ``` + $ export "NAME=value" + NAME=value: is not an identifier + ``` -Daniel Stenberg (28 Feb 2023) + This commit rewrites run-compiler's assignments and exports to work with + old /bin/sh, splitting assignment and export into two separate + statements, and only quote the value. So now we have: -- setopt: move the CURLOPT_CHUNK_DATA pointer to the set struct + ``` + NAME="value" + export NAME + ``` - To make duphandle work etc + While we're here, make the same change to the two supporting + assign+export lines preceeding the script to be consistent with how + exports work throughout the rest of configure.ac. - Closes #10635 + Closes #11228 -Viktor Szakats (28 Feb 2023) +Philip Heiduck (31 May 2023) -- quic/schannel: fix compiler warnings +- circleci: install impacket & wolfssl 5.6.0 - Fixes #10603 - Closes #10616 + Closes #11221 -Daniel Stenberg (28 Feb 2023) +Daniel Stenberg (31 May 2023) -- page-footer: add explanation for three missing exit codes +- tool_urlglob: use curl_off_t instead of longs - Added in 7.73.0, 7.77.0 and 7.84.0 + To handle more globs better (especially on Windows) - Closes #10630 + Closes #11224 -積丹尼 Dan Jacobson (28 Feb 2023) +Dan Fandrich (30 May 2023) -- rate.c: single URLs make no sense in --rate example +- scripts: Fix GHA matrix job detection in cijobs.pl - Here somehow you need to put more than one URL in these examples, else - they will make no sense, as --rate only affects the second and beyond - URLs. The first URL will always finish the same time no matter what - --rate is given. + The parsing is pretty brittle and it broke detecting some jobs at some + point. Also, detect if Windows is used in GHA. - Closes #10638 +- runtests: abort test run after failure without -a -Daniel Stenberg (28 Feb 2023) + This was broken in a recent refactor and test runs would not stop. -- libcurl-errors.3: add the CURLHcode errors from curl_easy_header.3 + Follow-up to d4a1b5b6 - Closes #10629 + Reported-by: Daniel Stenberg + Fixes #11225 + Closes #11227 -- mqtt: on send error, return error +Version 8.1.2 (30 May 2023) - Reported-by: Maciej Domanski +Daniel Stenberg (30 May 2023) - Closes #10623 +- RELEASE-NOTES: synced -- ws: keep the socket non-blocking + 8.1.2 release - Reported-by: marski on github - Fixes #10615 - Closes #10625 +- THANKS: contributors from 8.1.2 -- hostip: avoid sscanf and extra buffer copies +- lib1560: verify more scheme guessing - Also made create_hostcache_id() return the id length. + - on 2nd level domains + - on names without dots - Closes #10601 + As mentioned in #11161, "imap.com" will be guessed IMAP -- PARALLEL-TRANSFERS.md: not "early days" for this anymore + Closes #11219 - Refresh the language as the support is now over three years old +- page-header: minor wording polish in the URL segment - Closes #10624 + Closes #11217 -- easy: remove infof() debug leftover from curl_easy_recv +- page-header: mention curl version and how to figure out current release - It said "reached [path]/easy.c:1231" + Closes #11216 - Closes #10628 +- RELEASE-NOTES: synced -- idn: return error if the conversion ends up with a blank host +- configure: without pkg-config and no custom path, use -lnghttp2 - Some IDN sequences are converted into "" (nothing), which can make this - function end up with a zero length host name and we cannot consider that - a valid host to continue with. + Reported-by: correctmost on github + Fixes #11186 + Closes #11210 - Reported-by: Maciej Domanski - Closes #10617 +Stefan Eissing (28 May 2023) -- examples/http3.c: use CURL_HTTP_VERSION_3 +- curl: cache the --trace-time value for a second - and update the comment + - caches HH:MM:SS computed and reuses it for logging during + the same second. + - common function for plain log line start formatting - Closes #10619 + Closes #11211 -- x509asn1.c: use correct format specifier for infof() call +Kev Jackson (28 May 2023) - Detected by Coverity +- libcurl.m4: remove trailing 'dnl' that causes this to break autoconf - Closes #10614 + Closes #11212 -- Revert "GHA: add Microsoft C++ Code Analysis" +Stefan Eissing (26 May 2023) - This reverts commit e0db842b2a082dffad4a9fbe31321e9a75c74041. +- http3: send EOF indicator early as possible - This tool seems very restricted in how often it might be used by a - project and thus very quickly start to report fails simply because it - refuses to run when "there are more runs than allowed". + - ngtcp2 and quiche implementations relied on the DONE_SEND event + to forward the EOF for uploads to the libraries. This often + result in a last 0 length EOF data. Tracking the amount of + data left to upload allows EOF indication earlier. + - refs #11205 where CloudFlare DoH servers did not like to + receive the initial upload DATA without EOF and returned + a 400 Bad Request - Closes #10613 + Reported-by: Sergey Fionov + Fixes #11205 + Closes #11207 -Patrick Monnerat (25 Feb 2023) +Daniel Stenberg (26 May 2023) -- tests: test secure mail protocols with explicit SSL requests +- scripts/contri*sh: no longer grep -v ' ' - New tests 987, 988 and 989, disabled for rustls (hanging). + Originally these scripts filtered out names that have no space so that + they better avoid nick names not intended for credits. Such names are + not too commonly used, plus we now give credit even to those. - Closes #10077 + Additionally: non-latin names, like Asian, don't have spaces at all so + they were also filtered out and had to be manually added which made it + an error-prone operation where Asian names eventually easily fell off by + mistake. -- tests: support for imaps/pop3s/smtps protocols + Closes #11206 - Closes #10077 +- cf-socket: restore Curl_sock_assign_addr() -- runtests: use a hash table for server port numbers + Regression since it was not private. Also used by msh3.c - Closes #10077 + Follow-up to 8e85764b7bd7f05f5 + Reported-by: Gisle Vanem + Fixes #11202 + Closes #11204 -Andy Alt (25 Feb 2023) +- RELEASE-NOTES: synced -- INTERNALS.md: grammar + Taken down to 8.1.2 now for pending patch release - Closes #10607 +- libssh: when keyboard-interactive auth fails, try password -Daniel Stenberg (25 Feb 2023) + The state machine had a mistake in that it would not carry on to that + next step. -- RELEASE-NOTES: synced + This also adds a verbose output what methods that are available from the + server and renames the macros that change to the next auth methods to + try. -Philip Heiduck (25 Feb 2023) + Reported-by: 左潇峰 + Fixes #11196 + Closes #11197 -- .cirrus.yml: Bump to FreeBSD 13.2 +Emanuele Torre (25 May 2023) - Closes #10270 +- configure: fix build with arbitrary CC and LD_LIBRARY_PATH -- ngtcp2-gnutls.yml: bump to gnutls 3.8.0 + Since ./configure and processes that inherit its environment variables + are the only callers of the run-compiler script, we can just save the + current value of the LD_LIBRARY_PATH and CC variables to another pair of + environment variables, and make run-compiler a static script that + simply restores CC and LD_LIBRARY_PATH to the saved value, and before + running the compiler. - Closes #10507 + This avoids having to inject the values of the variables in the script, + possibly causing problems if they contains spaces, quotes, and other + special characters. -- CI: update ngtcp2 and nghttp2 for pytest + Also add exports in the script just in case LD_LIBRARY_PATH and CC are + not already in the environment. - Follow-up: https://github.com/curl/curl/commit/5c9ee8cef4b351a085b440f8178500 - 124647f8e6 + follow-up from 471dab2 - Closes #10508 + Closes #11182 -Andy Alt (25 Feb 2023) +Daniel Stenberg (25 May 2023) -- GHA: use same flags for Slackbuild as Slack package +- urlapi: remove superfluous host name check - Closes #10526 + ... as it is checked later more proper. -Daniel Stenberg (24 Feb 2023) + Closes #11195 -- rtsp: avoid sscanf for parsing +Stefan Eissing (25 May 2023) - Closes #10605 +- http2: fix EOF handling on uploads with auth negotiation -- http_proxy: parse the status line without sscanf + - doing a POST with `--digest` does an override on the initial request + with `Content-Length: 0`, but the http2 filter was unaware of that + and expected the originally request body. It did therefore not + send a final DATA frame with EOF flag to the server. + - The fix overrides any initial notion of post size when the `done_send` + event is triggered by the transfer loop, leading to the EOF that + is necessary. + - refs #11194. The fault did not happen in testing, as Apache httpd + never tries to read the request body of the initial request, + sends the 401 reply and closes the stream. The server used in the + reported issue however tried to read the EOF and timed out on the + request. - Closes #10602 + Reported-by: Aleksander Mazur + Fixes #11194 + Cloes #11200 -- telnet: error correctly for WS set to "x[num]" +Daniel Stenberg (23 May 2023) - Follow-up to e4f93be9d587 - Reported-by: Harry Sintonen - Closes #10606 +- RELEASE-NOTES: synced -- krb5: avoid sscanf for parsing + bump to 8.2.0 - Closes #10599 +- lib: remove unused functions, make single-use static -- misc: remove support for curl_off_t < 8 bytes + Closes #11174 - Closes #10597 +- scripts/singleuse.pl: add more API calls -- telnet: parse NEW_ENVIRON without sscanf +Christian Hesse (23 May 2023) - Closes #10596 +- configure: quote the assignments for run-compiler -- telnet: parse the WS= argument without sscanf + Building for multilib failed, as the compiler command contains an + extra argument. That needs quoting. - Closes #10596 + Regression from b78ca50cb3dda361f9c1 -- telnet: parse telnet options without sscanf + Fixes #11179 + Closes #11180 - Closes #10596 +Daniel Stenberg (23 May 2023) -- ftp: replace sscanf for MDTM 213 response parsing +- misc: fix spelling mistakes - Closes #10590 + Reported-by: musvaage on github + Fixes #11171 + Closes #11172 -- ftp: replace sscanf for PASV parsing +Version 8.1.1 (23 May 2023) - Closes #10590 +Daniel Stenberg (23 May 2023) -- ftp: make the EPSV response parser not use sscanf +- RELEASE-NOTES: synced - Closes #10590 + curl 8.1.1 -Stefan Eissing (24 Feb 2023) +- THANKS: contributors from the 8.1.1 release -- ngtcp2: fix unwanted close of file descriptor 0 +Dan Fandrich (22 May 2023) - ... causing macOS to hand out 0 as next socket handle and failing on - further operations. +- docs: fix fuzzing documentation link - Reported-by: Sergey Fionov - Fixes #10593 - Closes #10595 + Follow-up to 4c712a1b -Daniel Stenberg (23 Feb 2023) +- CI: add an Alpine build with MUSL -- select: stop treating POLLRDBAND as an error + MUSL is another libc implementation which has its own unique issues + worth testing. - POLLRDBAND does not seem to be an general error and on Windows the value - for POLLIN is 768 and the value for POLLRDBAND is 512. + Ref: #11140 + Closes #11178 - Fixes #10501 - Reported-by: opensslonzos-github on github - Closes #10592 +- runtests: add a missing \n at the end of a log message -- test978: mark file as text mode +correctmost on github (22 May 2023) - Follow-up to 4ea5702980cb +- SECURITY-PROCESS.md: link security advisory doc and fix typo - To fix test failures on Windows + Closes #11177 - Closes #10594 +Daniel Stenberg (22 May 2023) -- http: rewrite the status line parser without sscanf +- TODO: build curl with Windows Unicode support - Closes #10585 + Closes #7229 -- test978: verify that --stderr works for -w's stderr as well +- KNOWN_BUGS: hyper memory-leaks -Jay Satiro (23 Feb 2023) + Closes #10803 -- curl: make -w's %{stderr} use the file set with --stderr +Stefan Eissing (22 May 2023) - Reported-by: u20221022 on github - Fixes #10491 - Closes #10569 +- http/2: unstick uploads -- winbuild: fix makefile clean + - refs #11157 and #11175 where uploads get stuck or lead to RST streams + - fixes our h2 send behaviour to continue sending in the nghttp2 session + as long as it wants to. This will empty our send buffer as long as + the remote stream/connection window allows. + - in case the window is exhausted, the data remaining in the send buffer + will wait for a WINDOW_UPDATE from the server. Which is a socket event + that engages our transfer loop again + - the problem in the issue was that we did not exhaust the window, but + left data in the sendbuffer and no further socket events did happen. + The server was just waiting for us to send more. + - relatedly, there was an issue fixed that closing a stream with KEEP_HOLD + set kept the transfer from shutting down - as it should have - leading + to a timeout. - - Fix and move 'clean' code that removes the output and obj directories - trees from MakefileBuild.vc to Makefile.vc. + Closes #11176 - Prior to this change the 'clean' code did not work right because the - variables containing the directory names were not fully initialized and - the rmdir syntax was sometimes incorrect (typos). DIRDIST for example - was set to ..\builds\ and not ..\builds\$(CONFIG_NAME_LIB)\ so it would - remove the former and not the latter. If WITH_PREFIX was set then that - directory was removed instead. +Daniel Stenberg (21 May 2023) - Also, DIRDIST (the output directory) even if initialized should not be - removed by MakefileBuild.vc because by that time it could be set to a - user directory that may contain other files if WITH_PREFIX is set (eg we - don't want rmdir /s /q C:\usr\local). Therefore we remove from - Makefile.vc before any of that happens. I added a comment in both - makefiles explaining this. +- workflows/macos: add a job using gcc + debug + secure transport - Closes https://github.com/curl/curl/pull/10576 +Jay Satiro (21 May 2023) -- sectransp: fix compiler warning c89 mixed code/declaration +- lib: fix conversion warnings with gcc on macOS - Since cbf57176 the Cirrus CI 'macOS arm64 SecureTransport http2' has - been failing due to c89 warnings mixed code/declaration. That commit is - not the cause so I assume something has changed in the CI outside of our - configuration. Anyway, we don't mix code/declaration so this is the fix - for that. +Daniel Stenberg (21 May 2023) - Closes https://github.com/curl/curl/pull/10574 +- sectransp.c: make the code c89 compatible -Philipp Engel (22 Feb 2023) + Follow-up to dd2bb485521c2ec713001b3a -- BINDINGS: add Fortran binding + Reported-by: FeignClaims on github + Fixes #11155 + Closes #11159 - Closes #10589 +Emanuele Torre (21 May 2023) -Stefan Eissing (22 Feb 2023) +- Revert "urlapi: respect CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY for redirect + s" -- test2600: detect when ALARM_TIMEOUT is in use and adjust + This reverts commit df6c2f7b544f1f35f2a3e0be11f345affeb6fe9c. + (It only keep the test case that checks redirection to an absolute URL + without hostname and CURLU_NO_AUTHORITY). - - use higher timeout values > 1s - - skip duration checks + I originally wanted to make CURLU_ALLOW_SPACE accept spaces in the + hostname only because I thought + curl_url_set(CURLUPART_URL, CURLU_ALLOW_SPACE) was already accepting + them, and they were only not being accepted in the hostname when + curl_url_set(CURLUPART_URL) was used for a redirection. - Assisted-by: Marcel Raad - Closes #10513 + That is not actually the case, urlapi never accepted hostnames with + spaces, and a hostname with a space in it never makes sense. + I probably misread the output of my original test when I they were + normally accepted when using CURLU_ALLOW_SPACE, and not redirecting. -Daniel Stenberg (22 Feb 2023) + Some other URL parsers seems to allow space in the host part of the URL, + e.g. both python3's urllib.parse module, and Chromium's javascript URL + object allow spaces (chromium percent escapes the spaces with %20), + (they also both ignore TABs, and other whitespace characters), but those + URLs with spaces in the hostname are useless, neither python3's requests + module nor Chromium's window.location can actually use them. -- RELEASE-NOTES: synced + There is no reason to add support for URLs with spaces in the host, + since it was not a inconsistency bug; let's revert that patch before it + makes it into release. Sorry about that. -- test686: verify return code for no URL after --next + I also reverted the extra check for CURLU_NO_AUTHORITY since that does + not seem to be necessary, CURLU_NO_AUTHORITY already worked for + redirects. -- tool_operate: propagate error codes for missing URL after --next + Closes #11169 - Fixes #10558 - Reported-by: u20221022 on github - Closes #10580 +Dan Fandrich (20 May 2023) -- test1278: verify that an extra --no-remote-name cause no warning +- runtests: use the correct fd after select -- tool_getparam: don't add a new node for just --no-remote-name + The code was using the wrong fd when determining which runner was ready + with a response. - Unless --remote-name-all is used. + Ref: #10818 + Closes #11160 - Fixes #10564 - Reported-by: u20221022 on github - Closes #10582 +- test425: fix the log directory for the upload -- gen.pl: add '%GLOBALS' as a variable for mainpage + This must be %LOGDIR to let it work with parallel tests. - And use it in page-header to list all global command line options. + Ref: #10969 -- docs/cmdline-opts: mark all global options +- runtests: handle interrupted reads from IPC pipes - gen.pl now outputs a generic explanations for them for each option + These can be interrupted by signals, especially SIGINT to shut down, and + must be restarted so the IPC call arrives correctly. If the read just + returns an error instead, the IPC calling state will go out of sync and + a proper shutdown won't happen. - Fixes #10566 - Reported-by: u20221022 on github - Closes #10584 + Ref: #10818 -- GHA: add Microsoft C++ Code Analysis +Stefan Eissing (20 May 2023) - Closes #10583 +- http2: upload improvements -- tool_progress: shut off progress meter for --silent in parallel + Make send buffer smaller to have progress and "upload done" reporting + closer to reality. Fix handling of send "drain" condition to no longer + trigger once the transfer loop reports it is done sending. Also do not + trigger the send "drain" on RST streams. - Reported-by: finkjsc on github - Fixes #10573 - Closes #10579 + Background: + - a upload stall was reported in #11157 that timed out + - test_07_33a reproduces a problem with such a stall if the + server 404s the request and RSTs the stream. + - test_07_33b verifies a successful PUT, using the parameters + from #11157 and checks success -- lib1560: add a test using %25 in the userinfo in a URL + Ref: #11157 + Closes #11165 - Closes #10578 +- http2: increase stream window size to 10 MB -Stefan Eissing (21 Feb 2023) + Reported-by: pandada8 on github -- CURLOPT_PIPEWAIT: allow waited reuse also for subsequent connections + Fixes #11162 + Closes #11167 - As tested in test_02_07, when firing off 200 urls with --parallel, 199 - wait for the first connection to be established. if that is multiuse, - urls are added up to its capacity. +Daniel Stenberg (20 May 2023) - The first url over capacity opens another connection. But subsequent - urls found the same situation and open a connection too. They should - have waited for the second connection to actually connect and make its - capacity known. +- lib: rename struct 'http_req' to 'httpreq' - This change fixes that by + Because FreeBSD 14 kidnapped the name. + Ref: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271526 - - setting `connkeep()` early in the HTTP setup handler. as otherwise - a new connection is marked as closeit by default and not considered - for multiuse at all - - checking the "connected" status for a candidate always and continuing - to PIPEWAIT if no alternative is found. + Fixes #11163 + Closes #11164 - pytest: - - removed "skip" from test_02_07 - - added test_02_07b to check that http/1.1 continues to work as before +Viktor Szakats (20 May 2023) - Closes #10456 +- cmake: avoid `list(PREPEND)` for compatibility -Daniel Stenberg (21 Feb 2023) + `list(PREPEND)` requires CMake v3.15, our minimum is v3.7. -- test419: verify --dump-header to file that cannot be created + Ref: https://cmake.org/cmake/help/latest/command/list.html#prepend - Closes #10571 + Regression from 1e3319a167d2f32d295603167486e9e88af9bb4e -- tool_operate: avoid fclose(NULL) on bad header dump file + Reported-by: Keitagit-kun on Github + Fixes #11141 + Closes #11144 - Fixes #10570 - Reported-by: Jérémy Rabasco - Closes #10571 +Daniel Stenberg (19 May 2023) - RELEASE-NOTES: synced - Starting the journey towards 8.0.0 - -- cookie: parse without sscanf() +Stefan Eissing (19 May 2023) - Saves us from using 2*4096 bytes buffers on stack, the extra copies and - more. +- ngtcp2: proper handling of uint64_t when adjusting send buffer - Closes #10550 + Fixes #11149 + Closes #11153 -- lib517: verify time stamps without leading zeroes plus some more +- ngtcp2: fix compiler warning about possible null-deref -- parsedate: replace sscanf( for time stamp parsing + - compiler analyzer did not include the call context for this + static function where the condition had already been checked. + - eleminating the problem by making stream a call parameter - Closes #10547 + Fixes #11147 + Closes #11151 -- parsedate: parse strings without using sscanf() +Emanuele Torre (19 May 2023) - - sscanf is slow and complex, avoid it - - give up already if the string is 12 bytes or longer as no valid string - can be that long - - this can now be done without copy +- docs: document that curl_url_cleanup(NULL) is a safe no-op - Closes #10547 + This has always been the case, but it was not documented. -Matt Jolly (20 Feb 2023) + The paragraph was copied verbatim from curl_easy_cleanup.3 -- tests: HTTP server fixups + Closes #11150 - - httpserver.pl -> http-server.pl for consistency - - add http3-server.pl to EXTRA_DIST; alphabetise for maintainability - - nghttpx proxy invocation scripts should not use getcwd +Antoine Pitrou (19 May 2023) - Closes #10568 +- select: avoid returning an error on EINTR from select() or poll() -Version 7.88.1 (20 Feb 2023) + This was already done for the poll() and select() calls + made directly from Curl_poll(), but was missed in + Curl_wait_ms(), which is called when there are no fds + to wait on. -Daniel Stenberg (20 Feb 2023) + Fixes #11135 + Closes #11143 -- RELEASE-NOTES: synced +Daniel Stenberg (19 May 2023) - 7.88.1 release +- vquic.c: make recvfrom_packets static, avoid compiler warning -- THANKS: add contributors from 7.88.1 + warning: no previous prototype for 'recvfrom_packets' -- socketpair: allow EWOULDBLOCK when reading the pair check bytes + Reported-by: Keitagit-kun on github + Fixes #11146 + Closes #11148 - Reported-by: Gunamoi Software - Co-authored-by: Jay Satiro - Fixes #10561 - Closes #10562 +- urlapi: allow numerical parts in the host name -Jay Satiro (18 Feb 2023) + It can only be an IPv4 address if all parts are all digits and no more than + four parts, otherwise it is a host name. Even slightly wrong IPv4 will now be + passed through as a host name. -- tool_operate: fix scanbuild compiler warning + Regression from 17a15d88467 shipped in 8.1.0 - Prior to this change Azure CI scanbuild warned of a potential NULL - pointer string passed to strtol when CURLDEBUG enabled, even though the - way the code was written it wouldn't have happened. + Extended test 1560 accordingly. - Bug: https://github.com/curl/curl/commit/5479d991#r101159711 - Reported-by: Marcel Raad + Reported-by: Pavel Kalyugin + Fixes #11129 + Closes #11131 - Closes https://github.com/curl/curl/pull/10559 +Emilio Cobos Álvarez (19 May 2023) -- curl_setup: Suppress OpenSSL 3 deprecation warnings +- http2: double http request parser max line length - - Define OPENSSL_SUPPRESS_DEPRECATED. + This works around #11138, by doubling the limit, and should be a + relatively safe fix. - OpenSSL 3 has deprecated some of the functions libcurl uses such as - those with DES, MD5 and ENGINE prefix. We don't have replacements for - those functions so the warnings were disabled in autotools and cmake - builds, but still showed in other builds. + Ideally the buffer would grow as needed and there would be no need for a + limit? But that might be follow-up material. - Closes https://github.com/curl/curl/pull/10543 + Fixes #11138 + Closes #11139 -- build-openssl.bat: keep OpenSSL 3 engine binaries +Emanuele Torre (18 May 2023) - Prior to this change copying the OpenSSL 3 engine binaries failed - because 'engines-1_1' (OpenSSL 1.1.x folder name) was erroneously used - instead of 'engines-3'. The OpenSSL 3 builds would complete successfully - but without the engine binaries. +- configure: fix --help alignment - Closes https://github.com/curl/curl/pull/10542 + AC_ARG_ENABLE seems to only trim off whitespace from the start and end + of its help-string argument, while prepending two spaces of indentation + to all lines. -ALittleDruid (18 Feb 2023) + This means that the two spaces of indentation between the --enable-rtsp + and the --disable-rtsp line were not removed causing ./configure --help + to print: -- cmake: fix Windows check for CryptAcquireContext + Optional Features: + [...] + --enable-rtsp Enable RTSP support + --disable-rtsp Disable RTSP support - Check for CryptAcquireContext in windows.h and wincrypt.h only, since - otherwise this check may fail due to third party headers not found. + I removed the indentation to fix the issue, now it prints: - Closes https://github.com/curl/curl/pull/10353 + Optional Features: + [...] + --enable-rtsp Enable RTSP support + --disable-rtsp Disable RTSP support -Daniel Stenberg (19 Feb 2023) + The --enable-hsts and --disable-hsts lines had the same problems, and + have been fixed too. -- remote-header-name.d: mention that filename* is not supported + Closes #11142 - and that you can use --clobber to allow overwriting. +Deal(一线灵) (18 May 2023) - Ref: #10533 - Closes #10555 +- cmake: repair cross compiling - Co-authored-by: Jay Satiro + It cannot *run* code for testing purposes when cross-compiling. -Pierrick Charron (18 Feb 2023) + Closes #11130 -- CURLOPT_WS_OPTIONS.3: fix the availability version +Daniel Stenberg (18 May 2023) - Closes #10557 +- configure: generate a script to run the compiler -Jacob Hoffman-Andrews (18 Feb 2023) + in the CURL_RUN_IFELSE macro, with LD_LIBRARY_PATH set to the value of + the configure invoke, and not the value that might be used later, + intended for the execution of the output the compiler ouputs. -- GHA: update rustls dependency to 0.9.2 + For example when the compiler uses the same library (like libz) that + configure checks for. - This allows re-enabling test 312 for the rustls backend. + Reported-by: Jonas Bülow + Fixes #11114 + Closes #11120 - Closes #10553 +Stefan Eissing (18 May 2023) -Philip Heiduck (18 Feb 2023) +- cf-socket: completely remove the disabled USE_RECV_BEFORE_SEND_WORKAROUND -- HTTP3.md: update git branches + Closes #11118 - Closes #10554 +Emanuele Torre (18 May 2023) -Stefan Eissing (17 Feb 2023) +- urlapi: respect CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY for redirects -- urldata: remove `now` from struct SingleRequest - not needed - - Closes #10549 - -Daniel Stenberg (17 Feb 2023) - -- lib1560: add IPv6 canonicalization tests - - Closes #10552 + curl_url_set(uh, CURLUPART_URL, redirurl, flags) was not respecing + CURLU_ALLOW_SPACE and CURLU_NO_AUTHORITY in the host part of redirurl + when redirecting to an absolute URL. -- RELEASE-NOTES: synced + Closes #11136 -- urlapi: do the port number extraction without using sscanf() +Colin Cross (18 May 2023) - - sscanf() is rather complex and slow, strchr() much simpler +- hostip: move easy_lock.h include above curl_memory.h - - the port number function does not need to fully verify the IPv6 address - anyway as it is done later in the hostname_check() function and doing - it twice is unnecessary. + Similar to #9561, move easy_lock.h above curl_memory.h to fix building + against musl libc. - Closes #10541 + Closes #11140 -Stefan Eissing (17 Feb 2023) +Hind Montassif (18 May 2023) -- setopt: allow HTTP3 when HTTP2 is not defined +- curl_easy_getinfo: clarify on return data types - Reported-by: Karthikdasari0423 on github - Fixes #10538 - Closes #10544 + Closes #11126 -Jon Rumsey (17 Feb 2023) +Emanuele Torre (18 May 2023) -- os400: correct Curl_os400_sendto() +- checksrc: disallow spaces before labels - Add const qualifier to 5th argument of Curl_os400_sendto() + Out of 415 labels throughout the code base, 86 of those labels were + not at the start of the line. Which means labels always at the start of + the line is the favoured style overall with 329 instances. - Make OS400 wrapper for sendto match the normal prototype of sendto() - with a const qualifier. + Out of the 86 labels not at the start of the line: + * 75 were indented with the same indentation level of the following line + * 8 were indented with exactly one space + * 2 were indented with one fewer indentation level then the following + line + * 1 was indented with the indentation level of the following line minus + three space (probably unintentional) - Fixes #10539 - Closes #10548 + Co-Authored-By: Viktor Szakats -Stefan Eissing (17 Feb 2023) + Closes #11134 -- tests-httpd: add proxy tests +Daniel Stenberg (18 May 2023) - for direct and tunneling checks on http: and https: +- cookie: update the comment on cookie length and size limits - Closes #10519 + To refer to the proper cookie RFC and the upcoming RFC refresh. -Daniel Stenberg (17 Feb 2023) + Closes #11127 -- curl: make --silent work stand-alone +- url: provide better error message when URLs fail to parse - - renamed the struct field to 'silent' to match the cmdline option - - make --show-error toggle independently of --silent - - make --silent independent of ->noprogress as well + By providing the URL API error message into the error message. - By doing this, the three options --silent, --no-progress-meter and - --show-error should work independently of each other and also work with - and without '--no-' prefix as documented. + Ref: #11129 + Closes #11137 - Reported-by: u20221022 on github - Fixes #10535 - Closes #10536 +- RELEASE-NOTES: synced -- socks: allow using DoH to resolve host names + bumped to 8.1.1 - For SOCKS modes where a local host resolve is done. +Jon Rumsey (18 May 2023) - It was previously disabled in 12d655d4561, but a few local tests seem to - indicate that it works fine. Works now because of the SOCKS refactor of - 4a4b63daaa01ef59 that made it non-blocking. +- os400: update chkstrings.c - Reported-by: roughtex on github - Fixes #10537 - Closes #10540 + Compensate changes for recent changes to urldata.h to reclassify + STRING_AWS_SIGV4. -Stefan Eissing (17 Feb 2023) + Fixes #11132 + Closes #11133 -- test: add test for HTTP/2 corruption as reported in #10525 +Version 8.1.0 (17 May 2023) - - adding test_02_20 for reproducing the situation - - using recently released mod_h2 Apache module - - skipping test if an older version is installed - - adding installation of current mod_h2 to github pytest workflow +Daniel Stenberg (17 May 2023) - This reproduces the error reliable (for me) on the lib/http2.c version - of curl 7.88.0. And passes with the recent curl master. +- RELEASE-NOTES: synced - Closes #10534 +- THANKS: contributors from the 8.1.0 release -Daniel Stenberg (16 Feb 2023) +- hostip: include easy_lock.h before using GLOBAL_INIT_IS_THREADSAFE -- tool_operate: allow debug builds to set buffersize + Since that header file is the only place that define can be defined. - Using the CURL_BUFFERSIZE environment variable. + Reported-by: Marc Deslauriers - Closes #10532 + Follow-up to 13718030ad4b3209 -Stefan Eissing (16 Feb 2023) + Closes #11121 -- connnect: fix timeout handling to use full duration +Thomas Taylor (16 May 2023) - - connect timeout was used at half the configured value, if the - destination had 1 ip version 4 and other version 6 addresses - (or the other way around) - - extended test2600 to reproduce these cases +- aws-sigv4.d: fix region identifier in example - Reported-by: Michael Kaufmann - Fixes #10514 - Closes #10517 + Closes #11117 -Daniel Stenberg (16 Feb 2023) +Philip Heiduck (15 May 2023) -- tool_getparam: make --get a true boolean +- mlc_config.json: remove this linkcheck CI job config file - To match how it is documented in the man page. + Closes #11113 - Fixes #10527 - Reported-by: u20221022 on github - Closes #10531 +Daniel Silverstone (15 May 2023) -Harry Sintonen (16 Feb 2023) +- ssh: Add support for libssh2 read timeout -- http:: include stdint.h more readily + Hook the new (1.11.0 or newer) libssh2 support for setting a read timeout + into the SERVER_RESPONSE_TIMEOUT option. With this done, clients can use + the standard curl response timeout setting to also control the time that + libssh2 will wait for packets from a slow server. This is necessary to + enable use of very slow SFTP servers. - Closes #10516 + Signed-off-by: Daniel Silverstone -Stefan Eissing (16 Feb 2023) + Closes #10965 -- tests: make the telnet server shut down a socket gracefully +Osama Albahrani (14 May 2023) - - test 1452 failed occasionally with ECONNRESET errnos in curl when the - server closed the connection in an unclean state. +- GIT-INFO: add --with-openssl - Closes #10509 + Closes #11110 -Harry Sintonen (16 Feb 2023) +Daniel Stenberg (13 May 2023) -- http2: set drain on stream end +- RELEASE-NOTES: synced - Ensure that on_frame_recv() stream end will trigger a read if there is - pending data. Without this it could happen that the pending data is - never consumed. +Marcel Raad (13 May 2023) - This combined with https://github.com/curl/curl/pull/10529 should fix - https://github.com/curl/curl/issues/10525 +- md(4|5): don't use deprecated iOS functions - Ref: https://github.com/curl/curl/issues/10525 - Closes #10530 + They are marked as deprecated in iOS 13.0, which might result in + warnings-as-errors. -Stefan Eissing (16 Feb 2023) + Also, use `*_MIN_REQUIRED` instead of `*_MIN_ALLOWED`, which seems to + be what's currently used. -- http2: buffer/pausedata and output flush fix. + Bug: https://github.com/curl/curl/issues/11098 + Closes https://github.com/curl/curl/pull/11102 - * do not process pending input data when copying pausedata to the - caller - * return CURLE_AGAIN if the output buffer could not be completely - written out. +- md4: only build when used - Ref: #10525 - Closes #10529 + Its only usage in curl_ntlm_core.c is guarded by `USE_CURL_NTLM_CORE`, + so let's use this here too. -Marcel Raad (16 Feb 2023) + Ref: https://github.com/curl/curl/issues/11098 + Closes https://github.com/curl/curl/pull/11102 -- krb5: silence cast-align warning +Vítor Galvão (12 May 2023) - Add an intermediate cast to `void *`, as done everywhere else when - casting from `sockaddr *` to `sockaddr_in *`. +- write-out.d: Use response_code in example - Closes https://github.com/curl/curl/pull/10528 + Closes #11107 -Daniel Stenberg (15 Feb 2023) +Shohei Maeda (12 May 2023) -- RELEASE-NOTES: synced +- url: fix null dispname for --connect-to option - bumped to 7.88.1 + Closes #11106 -- tests: make sure gnuserv-tls has SRP support before using it +Daniel Stenberg (12 May 2023) - Reported-by: fundawang on github - Fixes #10522 - Closes #10524 +- test2306: verify getting a second response with folded headers -- runtests: fix "uninitialized value $port" + Reproduces the isue #11101 and verifies the fix. - by using a more appropriate variable + Verifies a17b2a503f - Reported-by: fundawang on github - Fixes #10518 - Closes #10520 +- headers: clear (possibly) lingering pointer in init -Version 7.88.0 (15 Feb 2023) + The "prevhead" pointer is used for the headers storage but was not + cleared correctly in init, which made it possible to act up when a + handle is reused. -Daniel Stenberg (15 Feb 2023) + Reported-by: Steve Herrell + Fixes #11101 + Closes #11103 - RELEASE-NOTES: synced - 7.88.0 release +- ngtcp2: use 0.15.0 -- THANKS: added contributors from 7.88.0 + - nghttp3 0.11.0 + - nghttp2 1.53.0 -- openssl: rename 'errcode_t' to 'sslerr_t' + Adapt to new API calls - Turns out "/usr/include/et/com_err.h" typedefs this type (without proper - variable scoping). + Closes #11031 - comerr is the "common error description library" that apparently might be use - d - by krb5 code, which then makes this header get used in a curl build. +Jay Satiro (10 May 2023) - Reported-by: Bruno Henrique Batista Cruz da Silva - Fixed #10502 - Closes #10500 +- openssl: fix indent -Dan Fandrich (13 Feb 2023) +Daniel Stenberg (10 May 2023) -- CONTRIBUTE: More formally specify the commit description +- CURLOPT_DNS_CACHE_TIMEOUT.3: fix spelling - This codifies what people have actually used in git commits over the - past 6 years. I've left off some lesser-used headers that appear to - duplicate others and tried to describe a consistent use for several - others that were used more arbitrarily. + Follow-up to 9ed7d56e044f5aa1b29 - This makes it easier for new committers to find out the kinds of things - we want to acknowledge, makes it easier to perform statistical analysis - on commits, and opens the possibility of performing lint checks on - descriptions before submission. + Closes #11096 - Reviewed-by: Daniel Stenberg - Reviewed-by: Jay Satiro +- hostip: use time_t for storing oldest DNS entry - Closes #10478 + Theoretically, the oldest time could overflow an int. In practice that + won't happen, but let's do this to please analyzers. -Stefan Eissing (13 Feb 2023) + Follow-up to 9ed7d56e044f5aa1b2928ccde6245d0 -- openssl: test and fix for forward proxy handling (non-tunneling). + Pointed out by Coverity. + Closes #11094 - - adding pytest test_10 cases for proxy httpd setup tests - - fixing openssl bug in https: proxy hostname verification that - used the hostname of the request and not the proxy name. +- http: free the url before storing a new copy - Closes #10498 + To avoid a memory-leak. -Daniel Stenberg (13 Feb 2023) + Reported-by: Hiroki Kurosawa -- cmdline-opts/Makefile: on error, do not leave a partial + Closes #11093 - And support 'make V=1' to show the full command line +- compressed.d: clarify the words on "not notifying headers" - Closes #10497 + Reported-by: Dylan Anthony + Fixes #11091 + Closes #11092 -- curl.1: make help, version and manual sections "custom" +- libssh2: free fingerprint better - Instead of using "multi: boolean", as these are slightly special as in - they do are not enable/disable ones. + Reported-by: Wei Chong Tan + Closes #11088 - Fixes #10490 - Reported-by: u20221022 on github - Closes #10497 +- CURLOPT_IPRESOLVE.3: clarify that this for host names, not IP addresses -Stefan Eissing (13 Feb 2023) + Reported-by: Harry Sintonen + Closes #11087 -- tests: add tests for HTTP/2 and HTTP/3 to verify the header API +- hostip: enforce a maximum DNS cache size independent of timeout value - Test 2403 and 2503 check "header_json" output and therefore use of - header-api + To reduce the damage an application can cause if using -1 or other + ridiculous timeout values and letting the cache live long times. - Closes #10495 + The maximum number of entries in the DNS cache is now totally + arbitrarily and hard-coded set to 29999. -Philip Heiduck (13 Feb 2023) + Closes #11084 -- CI: update wolfssl / wolfssh to 5.5.4 / 1.4.12 +- hostip: store dns timeout as 'int' - Closes #10493 + ... because it set and held as an 'int' elsewhere and can never be + larger. -Daniel Stenberg (13 Feb 2023) +- RELEASE-NOTES: synced -- KNOW_BUGS: cleanups with some changed to TODOs +- tool_operate: refuse (--data or --form) and --continue-at combo - - remove "Excessive HTTP/2 packets with TCP_NODELAY" + libcurl assumes that a --continue-at resumption is done to continue an + upload using the read callback and neither --data nor --form use + that and thus won't do what the user wants. Whatever the user wants + with this strange combination. - This is not a bug. Rather room for improvement. + Add test 426 to verify. - I believe these have been fixed: + Reported-by: Smackd0wn on github + Fixes #11081 + Closes #11083 - - 17.4 Connection failures with parallel HTTP/2 - - 17.5 HTTP/2 connections through HTTPS proxy frequently stall +- transfer: refuse POSTFIELDS + RESUME_FROM combo - - remove "FTPS needs session reuse" + The code assumes that such a resume is wanting to continue an upload + using the read callback, and since POSTFIELDS is done without callback + libcurl will just misbehave. - That is still true, but curl should also do session reuse now. + This combo will make the transfer fail with CURLE_BAD_FUNCTION_ARGUMENT + with an explanation in the error message. - - remove "ASCII FTP" + Reported-by: Smackd0wn on github + Fixes #11081 + Closes #11083 - It is documented behavior, and not single user has asked for extended - functionality here the last decade or so. +- ipv4.d/ipv6.d: they are "mutex", not "boolean" - - remove "Passive transfer tries only one IP address" + ... which for example means they do not have --no-* versions. - add as a TODO + Reported-by: Harry Sintonen + Fixes #11085 + Closes #11086 - - remove "DoH leaks memory after followlocation" +- docs/SECURITY-ADVISORY.md: how to write a curl security advisory - With a recipe on how to reproduce, this is pointless to keep around + Closes #11080 - - remove "DoH does not inherit all transfer options" +nobedee on github (5 May 2023) - add it as a TODO +- MANUAL.md: add dict example for looking up a single definition - Closes #10487 + Closes #11077 -Tatsuhiro Tsujikawa (13 Feb 2023) +Dan Fandrich (5 May 2023) -- GHA: bump ngtcp2 workflow dependencies +- runtests: fix -c option when run with valgrind - Closes #10494 + The curl binary argument wasn't being quoted properly. This seems to + have broken at some point after quoting was added in commit 606b29fe. -Patrick Monnerat (13 Feb 2023) + Reported-by: Daniel Stenberg + Ref: #11073 + Fixes #11074 + Closes #11076 -- content_encoding: do not reset stage counter for each header +- runtests: support creating more than one runner process - Test 418 verifies + The controller currently only creates and uses one, but more are now + possible. - Closes #10492 + Ref: #10818 -Daniel Stenberg (13 Feb 2023) +- runtests: spawn a new process for the test runner -- RELEASE-NOTES: synced + When the -j option is given, a new process is spawned in which the test + programs are run and from which test servers are started. Only one + process can be started at once, but this is sufficient to test that the + infrastructure can isolate those functions in a new task. There should + be no visible difference between the two modes at the moment. -Jay Satiro (13 Feb 2023) + Ref: #10818 + Closes #11064 -- multi: stop sending empty HTTP/3 UDP datagrams on Windows +- runtests: turn singletest() into a state machine - - Limit the 0-sized send procedure that is used to reset a SOCKET's - FD_WRITE to TCP sockets only. + This allows it to run in a non-blocking manner. - Prior to this change the reset was used on UDP sockets as well, but - unlike TCP sockets a 0-sized send actually sends out a datagram. + Ref: #10818 - Assisted-by: Marc Hörsken +- runtests: change runner interface to be asynchronous - Ref: https://github.com/curl/curl/pull/9203 + Program arguments are marshalled and then written to the end of a pipe + which is later read from and the arguments unmarshalled before the + desired function is called normally. The function return values are + then marshalled and written into another pipe when is later read from + and unmarshalled before being returned to the caller. - Fixes https://github.com/curl/curl/issues/9086 - Closes https://github.com/curl/curl/pull/10430 + The implementation is currently blocking but can be made non-blocking + without any changes to the API. This allows calling multiple runners + without blocking in the future. -Viktor Szakats (12 Feb 2023) + Ref: #10818 -- h3: silence compiler warnings +- runtests: call citest_finishtest in singletest - Reviewed-by: Daniel Stenberg - Fixes #10485 - Closes #10486 + This is where citest_starttest is called. -Daniel Stenberg (12 Feb 2023) + Ref: #10818 -- smb: return error on upload without size +- runtests: add a runner initialization function - The protocol needs to know the size ahead of time, this is now a known - restriction and not a bug. + This sets up the runner environment to start running tests. - Also output a clearer error if the URL path does not contain proper - share. + Ref: #10818 - Ref: #7896 - Closes #10484 +- runtests: remove directory from server filename variables -Viktor Szakats (12 Feb 2023) + There will soon be multiple log directories so the paths will no longer + be static in runtests.pl. Also, get rid of $SERVER2IN which was not + used. -- windows: always use curl's basename() implementation + Ref: #10818 - The `basename()` [1][2] implementation provided by mingw-w64 [3] makes - assumptions about input encoding and may break with non-ASCII strings. +- runtests: reduce package exports after refactoring - `basename()` was auto-detected with CMake, autotools and since - 68fa9bf3f5d7b4fcbb57619f70cb4aabb79a51f6 (2022-10-13), also in - `Makefile.mk` after syncing its behaviour with the mainline build - methods. A similar patch for curl-for-win broke official Windows - builds earlier, in release 7.83.1_4 (2022-06-15). + Some recent refactoring made these export no longer necessary. Also, + stop displaying the Unix socket paths at startup since there will soon + be many of them and they're not that interesting. - This patch forces all Windows builds to use curl's internal - `basename()` implementation to avoid such problems. + Ref: #10818 - [1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html - [2]: https://www.man7.org/linux/man-pages/man3/basename.3.html - [3]: https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-c - rt/misc/basename.c +- runtests: use a function to obtain $LOGDIR for a test - Reported-by: UnicornZhang on Github - Assisted-by: Cherish98 on Github - Reviewed-by: Daniel Stenberg + This will no longer be static soon. - Fixes #10261 - Closes #10475 + Ref: #10818 -Philip Heiduck (12 Feb 2023) +Jay Satiro (5 May 2023) -- Linux CI: Bump rustls-ffi to v0.9.1 +- tool_cb_hdr: Fix 'Location:' formatting for early VTE terminals - Closes #10476 + - Disable hyperlink formatting for the 'Location:' header value in VTE + 0.48.1 and earlier, since it is buggy in some of those versions. -Daniel Stenberg (12 Feb 2023) + Prior to this change those terminals may show the location header value + as gibberish or show it twice. -- libtest: build lib2305 with multibyte as well + Ref: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#backw + ard-compatibility - Fixes a build regression. + Fixes https://github.com/curl/curl/issues/10428 + Closes https://github.com/curl/curl/pull/11071 - Follow-up to 5a9a04d5567 - Reported-by: Viktor Szakats - Ref: https://github.com/curl/curl/pull/10475#issuecomment-1426831800 +François Michel (3 May 2023) - Closes #10477 +- quiche: disable pacing while pacing is not actually performed -Dmitry Atamanov (12 Feb 2023) + Closes #11068 -- cmake: fix dev warning due to mismatched arg +Daniel Stenberg (2 May 2023) - The package name passed to find_package_handle_standard_args (BROTLI) - does not match the name of the calling package (Brotli). This can lead - to problems in calling code that expects find_package result variables - (e.g., _FOUND) to follow a certain pattern. +- easy_cleanup: require a "good" handle to act - Closes https://github.com/curl/curl/pull/10471 + By insisting that the passed in handle is "good" (the magic number is + intact), this can limit the potential damage if a bad pointer is passed + in. Like when this function is called twice on the same handle pointer. -James Keast (11 Feb 2023) + Ref: #10964 + Closes #11061 -- setopt: Address undefined behaviour by checking for null +Andreas Falkenhahn (1 May 2023) - This addresses undefined behaviour found using clang's UBsan: +- amiga: Fix CA certificate paths for AmiSSL and MorphOS - curl/lib/setopt.c:177:14: runtime error: applying non-zero offset 1 to null p - ointer - SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior curl/lib/setopt.c:177 - :14 in + AmiSSL stores certificates in `AmiSSL:Certs` and MorphOS stores them in + `MOSSYS:Data/SSL/curl-ca-bundle.crt`. - Closes #10472 + Closes https://github.com/curl/curl/pull/11059 -Jacob Hoffman-Andrews (11 Feb 2023) +Daniel Stenberg (30 Apr 2023) -- rustls: improve error messages +- http2: (void)-mark when we explicitly ignore the return code - Changes numeric error codes into verbose error codes in two places. - Adds a prefix indicating that the error came from rustls, and in some - places which function it came from. + When h2_progress_egress() is called. Pointed out by Coverity. - Adds special handling for RUSTLS_RESULT_UNEXPECTED_EOF, since the - default message of "Unexpected EOF" is insufficiently explanatory. + Closes #11057 - Closes #10463 +- checksrc: find bad indentation in conditions without open brace -Daniel Stenberg (11 Feb 2023) + If the previous line starts with if/while/for AND ends with a closed + parenthesis and there's an equal number of open and closed parentheses + on that line, verify that this line is indented $indent more steps, if + not a cpp line. -- openssl: remove dead code + Also adjust the fall-out from this fix. - Follow-up to e8b00fcd6a + Closes #11054 - Due to the new 'if(!nonblocking)' check on the block a level above, - there is no need to check for it again within the same conditional. +Diogo Teles Sant'Anna (28 Apr 2023) - Detected by Coverity +- CI: Set minimal permissions on workflow ngtcp2-quictls.yml - Closes #10473 + Signed-off-by: Diogo Teles Sant'Anna -- ngtcp2: replace removed define and stop using removed function + Closes #11055 - They were removed upstream. +Dan Fandrich (28 Apr 2023) - Reported-by: Karthikdasari0423 on github - Fixes #10469 - Closes #10474 +- CI: use another glob syntax for matching files on Appveyor -- scripts/delta: show percent of number of files changed since last tag + The previous globbing syntax was not matching files recursively in + directories, so try appending a /* to more closely match the examples at + https://www.appveyor.com/docs/how-to/filtering-commits/ -- RELEASE-NOTES: synced +Daniel Stenberg (28 Apr 2023) -Stefan Eissing (10 Feb 2023) +- multi: add multi-ignore logic to multi_socket_action -- pytest: add a test case for PUSH related things. + The multi-ignore logic that was previously applied to + curl_multi_perform() (#10750) is here applied to the loop within + curl_multi_socket_action() to make it use the same optimization: most + handles have the same signal-ignore option state so this drastically + reduces the number of ignore/unignore calls per libcurl function invoke. - - checking that "103 Early Hints" are visible in curl's header dump file + Follow-up to bc90308328afb8 - Closes #10452 + Closes #11045 -Gregory Panakkal (10 Feb 2023) +Stefan Eissing (28 Apr 2023) -- WEBSOCKET.md: typo +- http2: do flow window accounting for cancelled streams - Fixing missing slash for ws protocol scheme + - nghttp2 does not free connection level window flow for + aborted streams + - when closing transfers, make sure that any buffered + response data is "given back" to the flow control window + - add tests test_02_22 and test_02_23 to reproduce - Closes #10464 + Closes #11052 -Stefan Eissing (10 Feb 2023) +- pingpong: fix compiler warning "assigning an enum to unsigned char" -- vquic: stabilization and improvements + Closes #11050 - vquic stabilization - - udp send code shared between ngtcp2 and quiche - - quiche handling of data and events improved +Daniel Stenberg (28 Apr 2023) - ngtcp2 and pytest improvements - - fixes handling of "drain" situations, discovered in scorecard - tests with the Caddy server. - - improvements in handling transfers that have already data or - are already closed to make an early return on recv +- configure: fix detection of apxs (for httpd) - pytest - - adding caddy tests when available + The condition check was turned the wrong way around! - scorecard improvemnts. - - using correct caddy port - - allowing tests for only httpd or caddy + Closes #11051 - Closes #10451 +Viktor Szakats (28 Apr 2023) -Philip Heiduck (10 Feb 2023) +- ci: `-Wno-vla` no longer necessary -- Linux CI: update some dependecies to latest tag + We handle this issue in the source now. - Closes #10458 + Follow-up to b725fe1944b45406676ea3aff333ae3085a848d9 -Daniel Stenberg (10 Feb 2023) + Reviewed-by: Marcel Raad + Reviewed-by: Daniel Stenberg + Closes #11048 -- test2305: send 3 frames, 4097 bytes each, as one message +Marcel Raad (28 Apr 2023) - Receive them using a 256 bytes buffer in a loop. +- tests/http: make curl_setup.h the first include -- ws: fix recv of larger frames + This is required for the macros there to take effect for system + libraries. Specifically, including the system libraries first led to + warnings about `_FILE_OFFSET_BITS` being redefined in curl_config.h on + the Solaris autobuilds for ws-data.c and ws-pingpong.c. + Also make the curl includes come first for the other source files here + for consistency. - + remove 'oleft' from the struct - + deal with "overflow data" in a separate dynbuf + Closes https://github.com/curl/curl/pull/11046 - Reported-by: Mike Duglas - Fixes #10438 - Closes #10447 +Emanuele Torre (27 Apr 2023) -- curl/websockets.h: extend the websocket frame struct +- checksrc: check for spaces before the colon of switch labels -- sws: fix typo, indentation add more ws logging + Closes #11047 -- test2304: remove stdout verification +Daniel Stenberg (27 Apr 2023) - This cripples the test somewhat but the check was bad since depending on - timing it could exit before the output was done, making the test flaky. +- RELEASE-NOTES: synced -Dan Fandrich (9 Feb 2023) +- libssh: tell it to use SFTP non-blocking -- CI: Add more labeler match patterns + Reported-by: Andreas Huebner + Fixes #11020 + Closes #11039 -- CI: Retry failed downloads to reduce spurious failures +Stefan Eissing (27 Apr 2023) - A temporary error with a remote server shouldn't cause a CI run to fail. - Also, put a cap on the time to download to fail faster on a misbehaving - server or connection and use HTTP compression where possible to reduce - download times. +- http2: enlarge the connection window -Daniel Stenberg (9 Feb 2023) + - fixes stalled connections -- no-clobber.d: only use long form options in man page text + - Make the connection window large enough, so that there is + some room left should 99/100 streams be PAUSED by the application - ... since they are expanded and the short-form gets mentioned - automatically so if the short form is mentioned as well, it gets - repeated. + Reported-by: Paweł Wegner + Fixes #10988 + Closes #11043 - Fixes #10461 - Closes #10462 - Reported-by: Dan Fandrich +Daniel Stenberg (27 Apr 2023) -- GHA: enable websockets in the torture job +- checksrc: fix SPACEBEFOREPAREN for conditions starting with "*" - Closes #10448 + The open paren check wants to warn for spaces before open parenthesis + for if/while/for but also for any function call. In order to avoid + catching function pointer declarations, the logic allows a space if the + first character after the open parenthesis is an asterisk. -- header.d: add a header file example + I also spotted what we did not include "switch" in the check but we should. - Closes #10455 + This check is a little lame, but we reduce this problem by not allowing + that space for if/while/for/switch. -Stefan Eissing (9 Feb 2023) + Reported-by: Emanuele Torre + Closes #11044 -- HTTP/[23]: continue upload when state.drain is set +- docs: minor polish - - as reported in #10433, HTTP/2 uploads may stall when a response is - received before the upload is done. This happens when the - data->state.drain is set for such a transfer, as the special handling - in transfer.c from then on only cared about downloads. - - add continuation of uploads, if applicable, in this case. - - add pytest case test_07_12_upload_seq_large to reproduce this scenario - (although, current nghttp2 implementation is using drain less often) + - "an HTTP*" (not "a") + - remove a few contractions + - remove a spurious "a" + - reduce use of "I" in texts - Reported-by: Lucas Pardue + Closes #11040 - Fixes #10433 - Closes #10443 +- ws: fix CONT opcode check -- http2: minor buffer and error path fixes + Detected by Coverity. Follow-up to 930c00c259 - - use memory buffer in full available size - - fail receive of reset/errored streams early + Closes #11037 - pytest: - - make test_05 error cases more reliable +Dan Fandrich (27 Apr 2023) - Closes #10444 +- CI: switch the awslc builds to build out-of-tree -Federico Pellegrin (9 Feb 2023) + This is a common configuration that should be tested to avoid + regressions. The awsls cmake build was already out-of-tree so the + automake build now joins it. -- openldap: fix missing sasl symbols at build in specific configs + Ref: #11006 - If curl is built with openldap support (USE_OPENLDAP=1) but does not - have also some other protocol (IMAP/SMTP/POP3) enabled that brings - in Curl_sasl_* functions, then the build will fail with undefined - references to various symbols: +- tests/http: fix out-of-tree builds - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_decode_mech' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_parse_url_auth - _option' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_cleanup' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_can_authentica - te' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_continue' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_start' - ld: ../lib/.libs/libcurl.so: undefined reference to `Curl_sasl_init' + Add both lib/ directories (src & build) to the search path so + curl_setup.h and its dependencies can be found. - This was tracked down to these functions bein used in openldap.c but - defined in curl_sasl.c and then forward in two vauth/ files to have - a guard against a set of #define configurations that was now extended - to cover also this case. + Followup-to acd82c8b - Example configuration targeted that could reproduce the problem: + Ref: #11006 + Closes #11036 - curl 7.87.1-DEV () libcurl/7.87.1-DEV .... OpenLDAP/2.6.3 - Protocols: file ftp ftps http https ldap ldaps +Daniel Stenberg (27 Apr 2023) - Closes #10445 +- urlapi: make internal function start with Curl_ -Daniel Stenberg (9 Feb 2023) + Curl_url_set_authority() it is. -- ws: use %Ou for outputting curl_off_t with info() + Follow-up to acd82c8bfd - Reported-by: Mike Duglas - Fixes #10439 - Closes #10441 + Closes #11035 -Jay Satiro (9 Feb 2023) +YX Hao (26 Apr 2023) -- curl_setup: Disable by default recv-before-send in Windows +- cf-socket: turn off IPV6_V6ONLY on Windows if it is supported - Prior to this change a workaround for Windows to recv before every send - was enabled by default. The way it works is a recv is called before - every send and saves the received data, in case send fails because in - Windows apparently that can wipe out the socket's internal received - data buffer. + IPV6_V6ONLY refs: + https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses + https://github.com/golang/go/blob/master/src/net/ipsock_posix.go + https://en.wikipedia.org/wiki/Unix-like + https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-o + ptions - This feature has led to several bugs because the way libcurl operates - it waits on a socket to read or to write, and may not at all times - check for buffered receive data. + default value refs: + https://datatracker.ietf.org/doc/html/rfc3493#section-5.3 + https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html#proc-sys-net + -ipv6-variables - Two recent significant bugs this workaround caused: - - Broken Schannel TLS 1.3 connections (#9431) - - HTTP/2 arbitrary hangs (#10253) + Closes #10975 - The actual code remains though it is disabled by default. Though future - changes to connection filter buffering could improve the situation IMO - it's just not tenable to manage this workaround. +Daniel Stenberg (26 Apr 2023) - Ref: https://github.com/curl/curl/issues/657 - Ref: https://github.com/curl/curl/pull/668 - Ref: https://github.com/curl/curl/pull/720 +- urldata: shrink *select_bits int => unsigned char - Ref: https://github.com/curl/curl/issues/9431 - Ref: https://github.com/curl/curl/issues/10253 + - dselect_bits + - cselect_bits - Closes https://github.com/curl/curl/pull/10409 + ... are using less than 8 bits. Changed types and moved them towards + the end of the structs to fit better. -Stefan Eissing (8 Feb 2023) + Closes #11025 -- http2: aggregate small SETTINGS/PRIO/WIN_UPDATE frames +Stefan Eissing (26 Apr 2023) - add a small buffer to nghttp2 session sending in order to aggregate - small SETTINGS/PRIO/WIN_UPDATE frames that nghttp2 "writes" to the - callback individually. +- tests/http: more tests with specific clients - Ref: #10389 - Closes #10432 + - Makefile support for building test specific clients in tests/http/clients + - auto-make of clients when invoking pytest + - added test_09_02 for server PUSH_PROMISEs using clients/h2-serverpush + - added test_02_21 for lib based downloads and pausing/unpausing transfers -- openssl: store the CA after first send (ClientHello) + curl url parser: + - added internal method `curl_url_set_authority()` for setting the + authority part of a url (used for PUSH_PROMISE) - move Curl_ssl_setup_x509_store() call after the first send (ClientHello) - this gives time to parse CA anchors while waiting on the server reply + http2: + - made logging of PUSH_PROMISE handling nicer - Ref: #10389 - Closes #10432 + Placing python test requirements in requirements.txt files + - separate files to base test suite and http tests since use + and module lists differ + - using the files in the gh workflows -Daniel Stenberg (8 Feb 2023) + websocket test cases, fixes for we and bufq + - bufq: account for spare chunks in space calculation + - bufq: reset chunks that are skipped empty + - ws: correctly encode frames with 126 bytes payload + - ws: update frame meta information on first call of collect + callback that fills user buffer + - test client ws-data: some test/reporting improvements -- RELEASE-NOTES: synced + Closes #11006 -Anthony Hu (8 Feb 2023) +Jay Satiro (26 Apr 2023) -- wolfssl: remove deprecated post-quantum algorithms +- libssh2: fix crash in keyboard callback - Closes #10440 + - Always set the libssh2 'abstract' user-pointer to the libcurl easy + handle associated with the ssh session, so it is always passed to the + ssh keyboard callback. -John Bampton (8 Feb 2023) + Prior to this change and since 8b5f100 (precedes curl 8.0.0), if libcurl + was built without CURL_DEBUG then it could crash during the ssh auth + phase due to a null dereference in the ssh keyboard callback. -- misc: fix spelling + Reported-by: Andreas Falkenhahn - Closes #10437 + Fixes https://github.com/curl/curl/pull/11024 + Closes https://github.com/curl/curl/pull/11026 -Daniel Stenberg (7 Feb 2023) +Daniel Stenberg (26 Apr 2023) -- man pages: call the custom user pointer 'clientp' consistently +- docs: clarify that more backends have HTTPS proxy support - The variable had a few different names. Now try to use 'clientp' - consistently for all man pages using a custom pointer set by the - application. + Closes #11033 - Reported-by: Gerrit Renker +- KNOWN_BUGS: remove two not-bugs - Fixes #10434 - Closes #10435 + - 11.7 signal-based resolver timeouts -- vtls: infof using %.*s needs to provide the length as int + Not considered a bug anymore but just implementation details. People + should avoid using timeouts with the synchronous name resolver. - Fixes a Coverity warning. + - 11.16 libcurl uses renames instead of locking for atomic operations - Closes #10436 + Not a bug, just a description of how it works -Stefan Eissing (7 Feb 2023) + Closes #11032 -- vrls: addressing issues reported by coverity +Harry Sintonen (26 Apr 2023) - I believe the code was secure before this, but limiting the accepted - name length to what is used in the structures should help Coverity's - analysis. +- hostip: add locks around use of global buffer for alarm() - Closes #10431 + When building with the sync name resolver and timeout ability we now + require thread-safety to be present to enable it. -Daniel Stenberg (7 Feb 2023) + Closes #11030 -- tool_operate: move the 'updated' variable +Daniel Stenberg (26 Apr 2023) - This was already done by Dan Fandrich in the previous PR but somehow I - lost that fixup. +- curl_path: bring back support for SFTP path ending in /~ - Follow-up to 349c5391f2121e + libcurl used to do a directory listing for this case (even though the + documentation says a URL needs to end in a slash for this), but + 4e2b52b5f7a3 modified the behavior. -Dan Fandrich (7 Feb 2023) + This change brings back a directory listing for SFTP paths that are + specified exactly as /~ in the URL. -- tool_operate: Fix error codes during DOS filename sanitize + Reported-by: Pavel Mayorov + Fixes #11001 + Closes #11023 - It would return CURLE_URL_MALFORMAT in an OOM condition. +Emanuele Torre (26 Apr 2023) - Closes #10414 +- docs/libcurl/curl_*escape.3: rename "url" argument to "input"/"string" -- tool_operate: Fix error codes on bad URL & OOM + Also reword the DESCRIPTION section to mention "input"/"string" argument + in bold. - curl would erroneously report CURLE_OUT_OF_MEMORY in some cases instead - of CURLE_URL_MALFORMAT. In other cases, it would erroneously return - CURLE_URL_MALFORMAT instead of CURLE_OUT_OF_MEMORY. Add a test case to - test the former condition. + Closes #11027 - Fixes #10130 - Closes #10414 +- docs/libcurl: minor cleanups -Daniel Stenberg (6 Feb 2023) + I was reading curl_unescape(3) and I noticed that there was an extra + space after the open parenthesis in the SYNOPSIS; I removed the extra + space. -- setopt: use >, not >=, when checking if uarg is larger than uint-max + I also ran a few grep -r commands to find and remove extra spaces + after '(' in other files, and to find and replace uses of `T*' instead + of `T *'. Some of the instances of `T*` where unnecessary casts that I + removed. - Closes #10421 + I also fixed a comment that was misaligned in CURLMOPT_SOCKETFUNCTION.3. -- vtls: fix failf() format argument type for %.*s handling + And I fixed some formatting inconsistencies: in curl_unescape(3), all + function parameter were mentioned with bold text except length, that was + mentioned as 'length'; and, in curl_easy_unescape(3), all parameters + were mentioned in bold text except url that was italicised. Now they are + all mentioned in bold. + Documentation is not very consistent in how function parameter are + formatted: many pages italicise them, and others display them in bold + text; but I think it makes sense to at least be consistent with + formatting within the same page. - Reported by Coverity + Closes #11027 - Closes #10422 +Daniel Stenberg (26 Apr 2023) -- openssl: fix "Improper use of negative value" +- man pages: simplify the .TH sections - By getting the socket first and returning error in case of bad socket. + - remove the version numbers + - simplify the texts - Detected by Coverity. + The date and version number will be put there for releases when maketgz + runs the updatemanpages.pl script. - Closes #10423 + Closes #11029 -Dan Fandrich (6 Feb 2023) +- hostcheck: fix host name wildcard checking -- packages: Remove Android.mk from makefile + The leftmost "label" of the host name can now only match against single + '*'. Like the browsers have worked for a long time. - This was missed in commit #44141512 + - extended unit test 1397 for this + - move some SOURCE variables from unit/Makefile.am to unit/Makefile.inc - Ref: #10418 + Reported-by: Hiroki Kurosawa + Closes #11018 -Daniel Stenberg (6 Feb 2023) +Dan Fandrich (25 Apr 2023) -- curl_ws_send.3: clarify how to send multi-frame messages +- smbserver: remove temporary files before exit -Mike Duglas (6 Feb 2023) + Each execution of test 1451 would leave a file in /tmp before. Since + Windows can't delete a file while it's open, all the temporary file + names are stored and deleted on exit. -- ws: fix multiframe send handling + Closes #10990 - Fixes #10413 - Closes #10420 +Stefan Eissing (25 Apr 2023) -Daniel Stenberg (6 Feb 2023) +- Websocket en-/decoding -- unit2600: make sure numerical curl_easy_setopt sets long + - state is fully kept at connection, since curl_ws_send() and + curl_ws_rec() have lifetime beyond usual transfers + - no more limit on frame sizes - Follow-up to 671158242db3203 + Reported-by: simplerobot on github + Fixes #10962 + Closes #10999 - Reported-by: Marcel Raad - Fixes #10410 - Closes #10419 +Patrick Monnerat (25 Apr 2023) -Andy Alt (6 Feb 2023) +- urldata: copy CURLOPT_AWS_SIGV4 value on handle duplication -- GHA: move Slackware test into matrix + Prior to this change STRING_AWS_SIGV4 (CURLOPT_AWS_SIGV4) was wrongly + marked as binary data that could not be duplicated. - Closes #10412 + Without this fix, this option's value is not copied upon calling + curl_easy_duphandle(). -Pronyushkin Petr (6 Feb 2023) + Closes https://github.com/curl/curl/pull/11021 -- urlapi: fix part of conditional expression is always true: qlen +Stefan Eissing (25 Apr 2023) - Closes #10408 +- http3: expire unpaused transfers in all HTTP/3 backends -- url: fix part of conditional expression is always true + Closes #11005 - Closes #10407 +- http2: always EXPIRE_RUN_NOW unpaused http/2 transfers -Daniel Stenberg (6 Feb 2023) + - just increasing the http/2 flow window does not necessarily + make a server send new data. It may already have exhausted + the window before -- RELEASE-NOTES: synced + Closes #11005 -Philip Heiduck (6 Feb 2023) +- http2: pass `stream` to http2_handle_stream_close to avoid NULL checks -- GHA/macos.yml: bump to gcc-12 + Closes #11005 - Closes #10415 +- h2/h3: replace `state.drain` counter with `state.dselect_bits` -Daniel Stenberg (6 Feb 2023) + - `drain` was used by http/2 and http/3 implementations to indicate + that the transfer requires send/recv independant from its socket + poll state. Intended as a counter, it was used as bool flag only. + - a similar mechanism exists on `connectdata->cselect_bits` where + specific protocols can indicate something similar, only for the + whole connection. + - `cselect_bits` are cleard in transfer.c on use and, importantly, + also set when the transfer loop expended its `maxloops` tries. + `drain` was not cleared by transfer and the http2/3 implementations + had to take care of that. + - `dselect_bits` is cleared *and* set by the transfer loop. http2/3 + does no longer clear it, only set when new events happen. -- packages: remove Android, update README + This change unifies the handling of socket poll overrides, extending + `cselect_bits` by a easy handle specific value and a common treatment in + transfers. - - Nobody builds curl for Android using this anymore - - Refreshed the README and converted to markdown + Closes #11005 - Reported-by: John Porter - Fixes #10416 - Closes #10418 +Daniel Stenberg (25 Apr 2023) -Kvarec Lezki (5 Feb 2023) +- socketpair: verify with a random value -- fopen: remove unnecessary assignment + ... instead of using the curl time struct, since it would use a few + uninitialized bytes and the sanitizers would complain. This is a neater + approach I think. - [CWE-1164] V1048: The '* tempname' variable was assigned the same value. + Reported-by: Boris Kuschel + Fixes #10993 + Closes #11015 - Ref: https://pvs-studio.com/en/docs/warnings/v1048/ +Stefan Eissing (25 Apr 2023) - Closes https://github.com/curl/curl/pull/10398 +- HTTP3: document the ngtcp2/nghttp3 versions to use for building curl -Gisle Vanem (5 Feb 2023) + - refs #11011 to clarify this for people building curl themselves -- libtest: add a sleep macro for Windows + Closes #11019 - .. because sleep() is used in some libtests. +Daniel Stenberg (25 Apr 2023) - Closes https://github.com/curl/curl/pull/10295 +- lib: unify the upload/method handling -Kvarec Lezki (3 Feb 2023) + By making sure we set state.upload based on the set.method value and not + independently as set.upload, we reduce confusion and mixup risks, both + internally and externally. -- http_aws_sigv4: remove typecasts from HMAC_SHA256 macro + Closes #11017 - V220: Suspicious sequence of types castings: memsize -> 32-bit integer -> mem - size. +- RELEASE-NOTES: synced - https://pvs-studio.com/en/docs/warnings/v220/ +Dan Fandrich (24 Apr 2023) - Closes #10400 +- CI: don't run CI jobs if only another CI was changed -Daniel Stenberg (3 Feb 2023) + A few paths were missed in the last commit, as well as a job added since + then. -- mailmap: Thomas1664 on github + Followup-to 395b9175 -Thomas1664 on github (3 Feb 2023) +- CI: adjust labeler match patterns -- CURLOPT_WRITEFUNCTION.3: fix memory leak in example +- runtests: support buffering log messages in runner & servers - Closes #10390 + Log messages generated with logmsg can now be buffered and returned from + the runner as a return value. This will be needed with parallel testing + to allow all messages for one test to be displayed together instead of + interspersed with messages of multiple tests. Buffering can be disabled + by setting a logging callback function with setlogfunc, which is + currently being done to preserve existing logging behaviour for now. -Kvarec Lezki (3 Feb 2023) + Some additional output is generated in verbose and debugprotocol modes, + which don't always use logmsg. These modes also impact some servers + which generate extra messages. No attempt is made to buffer everything + if these modes are enabled. -- doh: ifdef IPv6 code + Ref: #10818 + Closes #11016 - For disabled IPv6 a condition (conn->ip_version != CURL_IPRESOLVE_V4) is - always false. https://pvs-studio.com/en/docs/warnings/v560/ +- runtests: more consistently use logmsg in server control code - Closes #10397 + Also, display an error when sshversioninfo returns one. -Daniel Stenberg (3 Feb 2023) + Ref: #10818 -- urlapi: remove pathlen assignment +- runtests: create runner functions for clearlocks and stopservers - "Value stored to 'pathlen' is never read" + runtests.pl now uses runner for all server actions beyond the initial + variable configuration. - Follow-up to 804d5293f89 + Ref: #10818 - Reported-by: Kvarec Lezki +- runtests: tightened servers package exports - Closes #10405 - -Kvarec Lezki (3 Feb 2023) - -- http: fix "part of conditional expression is always false" - - [CWE-570] V560: A part of conditional expression is always false: conn->bits. - authneg. - [CWE-570] V560: A part of conditional expression is always false: conn->handl - er->protocol & (0 | 0). - - https://pvs-studio.com/en/docs/warnings/v560/ - - Closes #10399 - -Daniel Stenberg (2 Feb 2023) - -- urlapi: skip the extra dedotdot alloc if no dot in path - - Saves an allocation for many/most URLs. - - Updates test 1395 accordingly - - Closes #10403 - -Stefan Eissing (2 Feb 2023) - -- connections: introduce http/3 happy eyeballs - - New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing. - - filter is installed when `--http3` in the tool is used (or - the equivalent CURLOPT_ done in the library) - - starts a QUIC/HTTP/3 connect right away. Should that not - succeed after 100ms (subject to change), a parallel attempt - is started for HTTP/2 and HTTP/1.1 via TCP - - both attempts are subject to IPv6/IPv4 eyeballing, same - as happens for other connections - - tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT - - use a `soft` timeout at half the value. When the soft timeout - expires, the HTTPS-CONNECT filter checks if the QUIC filter - has received any data from the server. If not, it will start - the HTTP/2 attempt. - - HTTP/3(ngtcp2) improvements. - - setting call_data in all cfilter calls similar to http/2 and vtls filters - for use in callback where no stream data is available. - - returning CURLE_PARTIAL_FILE for prematurely terminated transfers - - enabling pytest test_05 for h3 - - shifting functionality to "connect" UDP sockets from ngtcp2 - implementation into the udp socket cfilter. Because unconnected - UDP sockets are weird. For example they error when adding to a - pollset. - - HTTP/3(quiche) improvements. - - fixed upload bug in quiche implementation, now passes 251 and pytest - - error codes on stream RESET - - improved debug logs - - handling of DRAIN during connect - - limiting pending event queue - - HTTP/2 cfilter improvements. - - use LOG_CF macros for dynamic logging in debug build - - fix CURLcode on RST streams to be CURLE_PARTIAL_FILE - - enable pytest test_05 for h2 - - fix upload pytests and improve parallel transfer performance. - - GOAWAY handling for ngtcp2/quiche - - during connect, when the remote server refuses to accept new connections - and closes immediately (so the local conn goes into DRAIN phase), the - connection is torn down and a another attempt is made after a short grace - period. - This is the behaviour observed with nghttpx when we tell it to shut - down gracefully. Tested in pytest test_03_02. - - TLS improvements - - ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, repl - aces - copy of logic in all tls backends. - - standardized the infof logging of offered ALPNs - - ALPN negotiated: have common function for all backends that sets alpn propr - ty - and connection related things based on the negotiated protocol (or lack the - reof). + The defaults are intended for runtests.pl, whereas runner.pm needs to + explicitly specify them. - - new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementatio - n. - Invoke: - python3 tests/tests-httpd/scorecard.py --help - for usage. +- runtests: display logs on server failure in singletest() - Improvements on gathering connect statistics and socket access. - - new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters - report connection statistics. This is triggered when the connection - has completely connected. - - new void Curl_pgrsTimeWas(..) method to report a timer update with - a timestamp of when it happend. This allows for updating timers - "later", e.g. a connect statistic after full connectivity has been - reached. - - in case of HTTP eyeballing, the previous changes will update - statistics only from the filter chain that "won" the eyeballing. - - new cfilter query CF_QUERY_SOCKET for retrieving the socket used - by a filter chain. - Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket() - for convenient use of this query. - - Change VTLS backend to query their sub-filters for the socket when - checks during the handshake are made. + This is closer to the place where logs are displayed on test failure. + Also, only display these logs if -p is given, which is the same flag + that controls display of test failure logs. Some server log files + need to be deleted later so that they stay around long enough to be + displayed on failure. - HTTP/3 documentation on how https eyeballing works. + Ref: #10818 - TLS improvements - - ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, repl - aces - copy of logic in all tls backends. - - standardized the infof logging of offered ALPNs - - ALPN negotiated: have common function for all backends that sets alpn propr - ty - and connection related things based on the negotiated protocol (or lack the - reof). +- runtests: turn a print into a logmsg - Scorecard with Caddy. - - configure can be run with `--with-test-caddy=path` to specify which caddy t - o use for testing - - tests/tests-httpd/scorecard.py now measures download speeds with caddy - - pytest improvements - - adding Makfile to clean gen dir - - adding nghttpx rundir creation on start - - checking httpd version 2.4.55 for test_05 cases where it is needed. Skippin - g with message if too old. - - catch exception when checking for caddy existance on system. - - Closes #10349 - -Daniel Stenberg (2 Feb 2023) - -- CODEOWNERS: remove the peeps mentioned as CI owners - - These owners do not have the bandwidth/energy to do the reviews which - makes PRs stall and this ownership claim flawed. We can bring people - back when the situation is different. - - Follow-up to c04c78ac87c4d46737934345a + Also enable another couple of useful messages in verbose mode. - Closes #10386 - -Martin D'Aloia (2 Feb 2023) + Ref: #10818 -- write-out.d: add 'since version' to %{header_json} documentation +Daniel Stenberg (24 Apr 2023) - The documentation of `%{header_json}` missed to mention since which - version this variable for `--write-out` is present. +- http: store the password in the correct variable - Based on commit https://github.com/curl/curl/commit/4133a69f2daa476bb - we can determine from the tags were this commit is present that the - first version to include it was `7.83.0`. - This could be also checked with: - `git tag --contains 4133a69f2daa476bb6d902687f1dd6660ea9c3c5` + Typo from fc2f1e547a4a, detected by Coverity (because there's dead code + due to this). - Closes #10395 + Closes #11002 -Daniel Stenberg (1 Feb 2023) +Stefan Eissing (24 Apr 2023) -- urlapi: avoid Curl_dyn_addf() for hex outputs +- HTTP3/quiche: terminate h1 response header when no body is sent - Inspired by the recent fixes to escape.c, we should avoid calling - Curl_dyn_addf() in loops, perhaps in particular when adding something so - simple as %HH codes - for performance reasons. This change makes the - same thing for the URL parser's two URL-encoding loops. + - fixes a failure in test2501 where a response without body was missing + the final empty line - Closes #10384 + Closes #11003 -- urlapi: skip path checks if path is just "/" +Dan Fandrich (22 Apr 2023) - As a miniscule optimization, treat a path of the length 1 as the same as - non-existing, as it can only be a single leading slash, and that's what - we do for no paths as well. +- runtests: move showdiff into runtests.pl - Closes #10385 + It's not used anywhere else. -Philip Heiduck (1 Feb 2023) +- devtest: add a new script for testing the test harness -- GHA/macos: use Xcode_14.0.1 for cmake builds + This is currently useful for starting a test server on its own without + an associated test, which can be used for interactive curl testing or + for validating parts of the test harness itself. More commands can be + added to perform additional functions in the future. - Fixes #10356 - Closes #10381 + Ref: #10818 + Closes #11008 -Viktor Szakats (1 Feb 2023) +- runtests: refactor the main test loop into two -- tls: fixes for wolfssl + openssl combo builds + The test loop now has an initial loop that first runs through all + possible tests to build a set of those to attempt on this run based on + features and keywords and only then goes through that new list to run + them. This actually makes it three loops through all tests cases, as + there is an existing loop that gathers possible test numbers from the + test files on disk. - 1. Add `USE_WOLFSSL` to the TLS backend priority list in - `lib/curl_ntlm_core.c`. + This has two minor effects on the output: all the tests that will be + skipped are displayed at the start (instead of being interspersed with + other tests) and the -l option no longer shows a count of tests at the + end or a (misleading) statement that tests have run successfully. The + skipped tests are also omitted from the test results sent to AppVeyor + and Azure in CI builds. - 2. Fix `lib/curl_ntlm_core.h` to respect TLS backend priority, bringing - it in sync with the above list and `lib/curl_ntlm_core.c` itself. + Another effect is a reduction in the amount of work considered part of + the "Test definition reading and preparation time" reported with -r + making those figures slightly lower than before. - Reported-by: Mark Roszko - Ref: https://github.com/curl/curl/issues/10321 + Ref: #10818 - 3. Allow enabling both wolfSSL and OpenSSL at the same time in - `lib/Makefile.mk` bringing this in line with cmake/autotools builds. - Update logic to select the crypto-specific lib for `ngtcp2`, which - supports a single TLS backend at the same time. +- runtests: track only the current test timings in runner.pm - Closes #10322 + This avoids passing these data through through global variables, which + soon won't be possible. -Daniel Stenberg (1 Feb 2023) + Ref: #10818 -- RELEASE-NOTES: synced +- runtests: skip test preprocessing when doing -l -- docs/INSTALL: document how to use multiple TLS backends + This speeds up the output tremendously by avoiding unnecessary work. - And document how OpenSSL forks and wolfSSL cannot be used at the same - time. +- runtests: simplify value returned regarding use of valgrind - Reported-by: Mark Roszko - Fixes #10321 - Closes #10382 + As a side effect this will now also show in verbose mode that valgrind + is being skipped on tests that explicitly disable it, such as 600. -Kvarec Lezki (1 Feb 2023) + Ref: #10818 -- cookies: fp is always not NULL +- runtests: fix quoting in Appveyor and Azure test integration - Closes #10383 + Test 1442's name was not quoted correctly so wasn't registered in + Appveyor and it had the wrong name in Azure. The JSON string quotes were + also invalid, even though both servers happened to accept it regardless. -Daniel Stenberg (31 Jan 2023) + Closes #11010 -- escape: use table lookup when adding %-codes to output +Daniel Stenberg (19 Apr 2023) - On my dev host, this code runs 7.8 times faster. +- RELEASE-NOTES: synced - Closes #10377 +Dan Fandrich (18 Apr 2023) -- unit2600: avoid error: ‘TEST_CASES’ defined but not used +- runtests: spread out the port numbers used by servers - Follow-up to d55de24dce9d51 + The server ports are chosen randomly for each server, but the random + ranges chosen were inconsistently-sized and overlapping. Now, they are + spread out more so at least the first random port chosen for each server + is guaranteed to not also be chosen by another server. The starting port + numbers are also raised to put them in the Ephemeral Port range—not the + range defined by RFC 6335 but the one used by Linux, which starts lower + and gives us more room to work with. - Closes #10379 + Reported-by: Daniel Stenberg -- escape: hex decode with a lookup-table +- runtests: fix problems on failure - Makes the decoding 2.8 times faster in my tests. + The verify time must be set in this case, like all cases. An error + message needs to be displayed as well. - Closes #10376 +- runtests: fix perl warning when is wrong -- cf-socket: fix build error wo TCP_FASTOPEN_CONNECT +- runtests: don't try to stop stunnel before trying again - Follow-up to 5651a36d1a + Calling stopserver() before retrying stunnel due to an error would stop + the dependent server (such as HTTP) meaning stunnel would have nothing + to talk to when it came up. Don't try to force a stop when it didn't + actually start. Also, don't mark the server as bad for future use when + it starts up on a retry. - Closes #10378 + Reported-by: eaglegai at github + Tested-by: eaglegai at github + Fixes #10976 - Reviewed-by: Stefan Eissing +- runtests: don't accidentally randomly choose the same port -Stefan Eissing (31 Jan 2023) + If a server couldn't be started on a port, a new one is randomly chosen + and the server is tried again. Avoid accidentally using a + randomly-chosen 0 port offset by adding 1 to the random number. -- CI: add pytest github workflow to CI test/tests-httpd on a HTTP/3 setup + Found-by: Daniel Stenberg - Closes #10317 +- runtests: don't attempt to use a port we know is in use -- connect: fix strategy testing for attempts, timeouts and happy-eyeball + This reduces the startup time when there is a known conflict on the + random port chosen for a server. This was already done for stunnel, but + now it's done for all servers. - - add test2600 as a unit test that triggers various connect conditions - and monitors behaviour, available in a debug build only. +- http-server: fix server name in a log message - - this exposed edge cases in connect.c that have been fixed + This changed when the file was renamed in commit cbf57176 - Closes #10312 +- runtests: refactor into more packages -- cf-socket: improvements in socket I/O handling + testutil.pm now contains a few miscellaneous functions that are used in + several places but have no better place to live. subvariables moves to + servers.pm since most variables that it substitutes relate to servers, + so this is the most appropriate place. Rename a few functions for better + naming consistency. - - Curl_write_plain/Curl_read_plain have been eliminated. Last code use - now uses Curl_conn_send/recv so that requests use conn->send/revc - callbacks which defaults to cfilters use. - - Curl_recv_plain/Curl_send_plain have been internalized in cf-socket.c. - - USE_RECV_BEFORE_SEND_WORKAROUND (active on Windows) has been moved - into cf-socket.c. The pre_recv buffer is held at the socket filter - context. `postponed_data` structures have been removed from - `connectdata`. - - the hanger in HTTP/2 request handling was a result of read buffering - on all sends and the multi handling is not prepared for this. The - following happens: + Ref: #10818 + Closes #10995 - - multi preforms on a HTTP/2 easy handle - - h2 reads and processes data - - this leads to a send of h2 data - - which receives and buffers before the send - - h2 returns - - multi selects on the socket, but no data arrives (its in the buffer alre - ady) - the workaround now receives data in a loop as long as there is something i - n - the buffer. The real fix would be for multi to change, so that `data_pendi - ng` - is evaluated before deciding to wait on the socket. +- runtests: call timestampskippedevents() in singletest - io_buffer, optional, in cf-socket.c, http/2 sets state.drain if lower - filter have pending data. + ..rather than by the runner - This io_buffer is only available/used when the - -DUSE_RECV_BEFORE_SEND_WORKAROUND is active, e.g. on Windows - configurations. It also maintains the original checks on protocol - handler being HTTP and conn->send/recv not being replaced. +- runtests: assume a newer Valgrind by default - The HTTP/2 (nghttp2) cfilter now sets data->state.drain when it finds - out that the "lower" filter chain has still pending data at the end of - its IO operation. This prevents the processing from becoming stalled. + The tests for an older Valgrind version should probably just be deleted, + given that they're testing for an 18-year-old version. - Closes #10280 +- runtests: refactor test runner code into runner.pm -Daniel Stenberg (31 Jan 2023) + This is code that is directly responsible for running a single test. + This will eventually run in a separate process as part of the parallel + testing project. -- openssl: only use CA_BLOB if verifying peer + Ref: #10818 - Reported-by: Paul Groke - Bug: https://curl.se/mail/lib-2023-01/0070.html - Fixes #10351 - Closes #10359 +- runtests: skip unneeded work if test won't be running -Thomas1664 on github (31 Jan 2023) + This speeds up tests by avoiding unnecessary processing. -- curl_free.3: fix return type of `curl_free` + Ref: #10818 - Fixes #10373 - Closes #10374 +- runtests: factor out singletest_postcheck -Daniel Stenberg (30 Jan 2023) + This will eventually need to be part of the test runner. -- zuul: stop using this CI service + Ref: #10818 - The important jobs have already transitioned. The remaining ones we can - skip for now. +- test303: kill server after test - Closes #10368 + Otherwise, an HTTP test closely following this one with a tight time + constraint (e.g. 672) could fail because the test server stays sitting + with the wait command for a while. -- copyright: remove "m4/ax_compile_check_sizeof.m4" from skips +Patrick Monnerat (18 Apr 2023) - and report if skipped files do not exist. +- OS400: provide ILE/RPG usage examples - Follow-up to 9e11c2791fb960758 which removed the file. + Closes https://github.com/curl/curl/pull/10994 - Closes #10369 +- OS400: improve vararg emulation -- ws: unstick connect-only shutdown + - Use V7R4 RPG procedure overloading to improve vararg emulation. - As this mode uses blocking sockets, it must set them back to - non-blocking in disconnect to avoid the risk of getting stuck. + From OS400 V7R4 and above, ILE/RPG implements a limited procedure + overloading feature that can be used to improve curl's typed + implementation of varargs procedures. This commit applies it to + curl_easy_setopt(), curl_multi_setopt(), curl_share_setopt() and + curl_easy_getinfo(). - Closes #10366 + Closes https://github.com/curl/curl/pull/10994 -- ws: remove bad assert +- OS400: fix and complete ILE/RPG binding - Reported-by: Stanley Wucw - Fixes #10347 - Closes #10366 + - Fix wrong definitions of CURL_ZERO_TERNINATED, curl_mime_data() and + curl_mime_data_ccsid(). -- openssl: adapt to boringssl's error code type + - Add recent definitions, in particular blob, header API and WebSockets + API. - BoringSSL uses uint32_t, OpenSSL uses 'unsigned 'long' + - Support for CURLVERSION_ELEVENTH. - Closes #10360 + - New functions for EBCDIC support. -- tool_operate: repair --rate + Reflect these changes in README.OS400. - Regression from a55256cfb242 (7.87.0) - Reported-by: highmtworks on github - Fixes #10357 - Closes #10358 + Closes https://github.com/curl/curl/pull/10994 -- dict: URL decode the entire path always +- OS400: implement EBCDIC support for recent features - Reported-by: dekerser on github - Fixes #10298 - Closes #10354 + - Support CURLVERSION_ELEVENTH. -Stefan Eissing (29 Jan 2023) + - New function curl_url_strerror_ccsid(). -- vtls: do not null-check when we already assume cf-ctx exists + - curl_easy_setopt_ccsid() supports blobs and 3 recent string options. - Fixes #10361 - Closes #10362 + - New function curl_easy_header_ccsid(). -Daniel Stenberg (29 Jan 2023) + - New generic latin1<-->ccsid conversion functions curl_from_ccsid() and + curl_to_ccsid() for user convenience. -- RELEASE-NOTES: synced + - README.OS400 updated accordingly. -- CURLOPT_READFUNCTION.3: the callback 'size' arg is always 1 + - Removed a leftover QsoSSL support identifier. - Reported-by: Brian Green - Fixes #10328 - Closes #10355 + Closes https://github.com/curl/curl/pull/10994 -- copyright.pl: cease doing year verifications +- OS400: rework build scripts - As we have (mostly) removed the copyright year ranges. + - Rename shell function "system" to "CLcommand" to avoid confusion with + built-in command. - Reported-by: Ryan Schmidt - Fixes #10345 - Closes #10352 + - Reformat scripts. Fix some indentations. Avoid lines > 80 characters + where possible. -Dan Fandrich (28 Jan 2023) + - Support ASCII runtime development files in a user-defined directory + path. -- CI: Work around a labeler bug that removes labels + - FIX SONAME detection. -Jay Satiro (26 Jan 2023) + - Drop form API test program compilation (does not exist anymore). -- write-out.d: clarify Windows % symbol escaping + Closes https://github.com/curl/curl/pull/10994 - - Clarify that in Windows batch files the % must be escaped as %%, and - at the command prompt it cannot be escaped which could lead to - incorrect expansion. +Sevan Janiyan (18 Apr 2023) - Prior to this change the doc implied % must be escaped as %% in win32 - always. +- tests/sshserver.pl: Define AddressFamily earlier - --- + As the comment states "Address family must be specified before ListenAddress" + , otherwise the tests fail to run + `"failed starting SSH server" 52 times (582, 583, 600, 601, 602, 603, 604, 60 + 5, 606 and 43 more)` - Examples showing how a write-out argument is received by curl: + Closes #10983 - If curl --write-out "%{http_code}" is executed in a batch file: - {http_code} +Stefan Eissing (18 Apr 2023) - If curl --write-out "%%{http_code}" is executed in a batch file: - %{http_code} +- quiche: Enable IDLE egress handling - If curl --write-out "%{http_code}" is executed from the command prompt: - %{http_code} + Follow-up to 544abeea which added the handling but wrongly left it + commented out. - If curl --write-out "%%{http_code}" is executed from the command prompt: - %%{http_code} + Closes https://github.com/curl/curl/pull/11000 - At the command prompt something like "%{speed_download}%{http_code}" - would first be parsed by the command interpreter as %{speed_download}% - and would be expanded as environment variable {speed_download} if it - existed, though that's highly unlikely since Windows environment names - don't use braces. +Daniel Stenberg (18 Apr 2023) - --- +- docs/examples/protofeats.c: Outputs all protocols and features - Reported-by: Muhammad Hussein Ammari + Showing off one way to get to char pointer arrays of info returned by + curl_version_info() - Ref: https://github.com/bagder/everything-curl/pull/279 + Closes #10991 - Fixes https://github.com/curl/curl/issues/10323 - Closes https://github.com/curl/curl/pull/10337 +- tests/keywords.pl: remove -Ryan Schmidt (26 Jan 2023) + This script does not work since the introduction of the test + preprocessing. If we need this functionality, it probably needs to be + moved into the runtests tool or similar. -- connect: Fix build when not ENABLE_IPV6 + Reported-by: Dan Fandrich + Fixes #10895 + Closes #10987 - Check for ENABLE_IPV6 before accessing AF_INET6. Fixes build failure - introduced in 1c5d8ac. +Stefan Eissing (17 Apr 2023) - Closes https://github.com/curl/curl/pull/10344 +- http2: support HTTP/2 to forward proxies, non-tunneling -- cf-socket: Fix build when not HAVE_GETPEERNAME + - with `--proxy-http2` allow h2 ALPN negotiation to + forward proxies + - applies to http: requests against a https: proxy only, + as https: requests will auto-tunnel + - adding a HTTP/1 request parser in http1.c + - removed h2h3.c + - using new request parser in nghttp2 and all h3 backends + - adding test 2603 for request parser + - adding h2 proxy test cases to test_10_* - Remove remaining references to conn and sockfd, which were removed from - the function signature when conninfo_remote was renamed to - conn_set_primary_ip in 6a8d7ef. + scorecard.py: request scoring accidentally always run curl + with '-v'. Removed that, expect double numbers. - Closes https://github.com/curl/curl/pull/10343 + labeller: added http1.* and h2-proxy sources to detection -Stefan Eissing (26 Jan 2023) + Closes #10967 -- vtls: Manage current easy handle in nested cfilter calls +Daniel Stenberg (17 Apr 2023) - The previous implementation cleared `data` so the outer invocation lost - its data, which could lead to a crash. +- curl_easy_unescape.3: rename the argument - Bug: https://github.com/curl/curl/issues/10336 - Reported-by: Fujii Hironori + and highlight it appropriately in the text. - Closes https://github.com/curl/curl/pull/10340 + Closes #10979 -Dan Fandrich (25 Jan 2023) +Viktor Szakats (17 Apr 2023) -- CI: Add even more paths to the labeler config (#10326) +- autotools: sync up clang picky warnings with cmake -- scripts: Fix Appveyor job detection in cijobs.pl + Bringing missing options over from CMake. - The reorganization in #9769 broke the script. This should probably be - rewritten to use a YAML parser for better upward compatibility. + Move around existing `-Wno-pointer-bool-conversion` option to come + _after_ `-Wconversion`. -- CI: Add a few more paths to the labeler config (#10326) + Reviewed-by: Marcel Raad + Closes #10974 -- CI: Switch the labeler event to pull_request_target +Daniel Stenberg (17 Apr 2023) - Otherwise, the action won't work on PRs from forked repositories - (#10326). +- tests/libtest/lib1900.c: remove -Viktor Szakats (25 Jan 2023) + This file was left behind when the rest of the test was previously removed. -- cmake: delete redundant macro definition `SECURITY_WIN32` + Follow-up to e50a877df74f - Stop explicitly defining `SECURITY_WIN32` in CMake builds. +- src/tool_operhlp.c: fix value stored to 'uerr' is never read - No other build systems define this macro, because it's unconditionally - defined in `lib/curl_sspi.h` already. This is the only curl source using - the `sspi.h` and `security.h` Win32 headers, and no other Win32 headers - need this macro. + Ref: https://github.com/curl/curl/pull/10974#issuecomment-1510461343 + Reported-by: Viktor Szakats + Closes #10982 - Reviewed-by: Jay Satiro - Closes #10341 +Viktor Szakats (16 Apr 2023) -Fredrik (24 Jan 2023) +- cmake: speed up and extend picky clang/gcc options -- winbuild: document that arm64 is supported + Extend existing picky compiler options with ones missing compared to + autotools builds. Also sync options between clang and gcc. - Building an arm64 version works flawlessly with the VS arm64 toolset. + Redesign the way we enable these options to avoid the slow option + detection almost completely. - Closes https://github.com/curl/curl/pull/10332 + This reduces the number of detections from 35 to zero for clang and + 3 for gcc, even after adding a bunch of new options. -Cherish98 (24 Jan 2023) + clang 3.0 (2011-11-29) and gcc 2.95 (1999-07-31) now required. -- openssl: don't log raw record headers + Also show enabled picky options. - - Skip content type SSL3_RT_HEADER in verbose TLS output. + Ref: https://github.com/libssh2/libssh2/pull/952 - This commit prevents bogus and misleading verbose TLS header messages as - discussed in #10299. + Reviewed-by: Daniel Stenberg + Closes #10973 - Assisted-by: Peter Wu +Andreas Falkenhahn (16 Apr 2023) - Closes https://github.com/curl/curl/pull/10299 +- nbtlm: use semicolons instead of commas for (void) args -Marc Aldorasi (24 Jan 2023) + Closes #10978 -- cmake: use list APPEND syntax for CMAKE_REQUIRED_DEFINITIONS +Daniel Stenberg (15 Apr 2023) - - Use list() instead of set() for CMAKE_REQUIRED_DEFINITIONS list since - the former is clearer. +- multi: free up more data earleier in DONE - Closes https://github.com/curl/curl/pull/10272 + Before checking for more users of the connection and possibly bailing + out. -Dan Fandrich (23 Jan 2023) + Fixes #10971 + Reported-by: Paweł Wegner + Closes #10972 -- CI: Add a workflow to automatically label pull requests +- RELEASE-NOTES: synced - The labeler language is quite restrictive right now so labels are added - quite conservatively, meaning that many PRs won't get labels when it's - "obvious" they should. It will still save some manual work on those - that it can label. +- curl: do NOT append file name to path for upload when there's a query -Jay Satiro (21 Jan 2023) + Added test 425 to verify. -- system.h: assume OS400 is always built with ILEC compiler + Reported-by: Dirk Rosenkranz + Bug: https://curl.se/mail/archive-2023-04/0008.html + Closes #10969 - Prior to this change the OS400 types were only defined when __ILEC400__. - That symbol is only defined by IBM's C compiler and not their C++ - compiler, which led to missing types when users on OS400 would compile a - C++ application that included curl. +- libcurl-thread.3: improved name resolver wording - The IBM C and C++ compilers are the only native compilers on the - platform. + And make better .SH sections - Assisted-by: Jon Rumsey - Reported-by: John Sherrill + Closes #10966 - Fixes https://github.com/curl/curl/issues/10305 - Closes https://github.com/curl/curl/pull/10329 +Colman Mbuya (14 Apr 2023) -xgladius (20 Jan 2023) +- CURLOPT_PROXY_SSL_VERIFYPEER.3: fix minor grammar mistake -- cmake: Remove deprecated symbols check + Closes #10968 - curl stopped use of CMAKE_USE_ as a prefix for its own build symbols in - 2021 and added a check, meant to last 1 year, to fatally error on those - symbols. This commit removes that check. +Daniel Stenberg (14 Apr 2023) - Closes https://github.com/curl/curl/pull/10314 +- curl: add --proxy-http2 -Dan Fandrich (20 Jan 2023) + For trying HTTP/2 with an HTTPS proxy. -- docs: POSTFIELDSIZE must be set to -1 with read function + Closes #10926 - Reported-by: RanBarLavie on github +- KNOWN_BUGS: remove fixed or outdated issues, move non-bugs - Closes #10313 + - remove h3 issues believed to be fixed -Stefan Eissing (20 Jan 2023) + - make the flaky CI issue be generic and not Windows specific -- vtls: fix hostname handling in filters + - "TLS session cache does not work with TFO" now documented - - Copy the hostname and dispname to ssl_connect_data. + This is now a documented restriction and not a bug. TFO in general is + rarely used and has other problems, making it a low-priotity thing to + work on. - Use a copy instead of referencing the `connectdata` instance since this - may get free'ed on connection reuse. + - remove "Renegotiate from server may cause hang for OpenSSL backend" - Reported-by: Stefan Talpalaru - Reported-by: sergio-nsk@users.noreply.github.com + This is an OpenSSL issue, not a curl one. Even if it taints curl. - Fixes https://github.com/curl/curl/issues/10273 - Fixes https://github.com/curl/curl/issues/10309 + - rm "make distclean loops forever" - Closes https://github.com/curl/curl/pull/10310 + - rm "configure finding libs in wrong directory" -Sergey Bronnikov (17 Jan 2023) + Added a section to docs/INSTALL.md about it. -- lib: fix typos + - "A shared connection cache is not thread-safe" - Closes https://github.com/curl/curl/pull/10307 + Moved over to TODO and expanded for other sharing improvements we + could do -- curl_version_info.3: fix typo + - rm "CURLOPT_OPENSOCKETPAIRFUNCTION is missing" - Closes https://github.com/curl/curl/pull/10306 + - rm "Blocking socket operations in non-blocking API" -Jay Satiro (17 Jan 2023) + Already listed as a TODO -- openssl: Don't ignore CA paths when using Windows CA store (redux) + - rm "curl compiled on OSX 10.13 failed to run on OSX 10.10" - .. and remove 'experimental' designation from CURLSSLOPT_NATIVE_CA. + Water under the bridge. No one cares about this anymore. - This commit restores the behavior of CURLSSLOPT_NATIVE_CA so that it - does not override CURLOPT_CAINFO / CURLOPT_CAPATH, or the hardcoded - default locations. Instead the native Windows CA store can be used at - the same time. + - rm "build on Linux links libcurl to libdl" - --- + Verified to not be true (anymore). - This behavior was originally added over two years ago in abbc5d60 - (#5585) but then 83393b1a (#7892) broke it over a year ago, I assume - inadvertently. + - rm "libpsl is not supported" - The CURLSSLOPT_NATIVE_CA feature was marked experimental and likely - rarely used. + The cmake build supports it since cafb356e19cda22 - Ref: https://github.com/curl/curl/pull/5585 - Ref: https://github.com/curl/curl/pull/7892 - Ref: https://curl.se/mail/lib-2023-01/0019.html + Closes #10963 - Closes https://github.com/curl/curl/pull/10244 +- url: fix PVS nits -Daniel Stenberg (13 Jan 2023) + - expression 'hostptr' is always true + - a part of conditional expression is always true: proxypasswd + - expression 'proxyuser' is always true + - avoid multiple Curl_now() calls in allocate_conn -- RELEASE-NOTES: synced + Ref: #10929 + Closes #10959 -- ws: fix autoping handling +- bufq: simplify since expression is always true - Reported-by: Alexey Savchuk - Fixes #10289 - Closes #10294 + The check for 'len' is already done so it will remain true until + updated. Pointed out by PVS. -- curl_log: avoid printf() format checking with mingw + Ref: #10929 + Closes #10958 - Since it does not seem to like %zu and more +- hash: fix assigning same value - Follow-up to db91dbbf2 + Pointed out by PVS - Fixes #10291 - Closes #10292 + Ref: #10929 + Closes #10956 -- tool_getparam: fix compiler warning when !HAVE_WRITABLE_ARGV +- cookie: address PVS nits - Follow-up to 2ed0e1f70ee176edf3d2 + - avoid assigning the same value again + - remove superfluous check of co->domain + - reduce variable scope for namep/valuep - Closes #10286 + Ref: #10929 + Closes #10954 -Stefan Eissing (12 Jan 2023) +Stefan Eissing (14 Apr 2023) -- openssl: make the BIO_METHOD a local variable in the connection filter +- cf-socket: Disable socket receive buffer by default - This avoids UAF issues when `curl_global_cleanup()` is called before all - transfers have been completely handled. Unfortunately this seems to be a - more common pattern than we like. + - Disable socket receive buffer unless USE_RECV_BEFORE_SEND_WORKAROUND + is in place. - Closes #10285 + While we would like to use the receive buffer, we have stalls in + parallel transfers where not all buffered data is consumed and no socket + events happen. -Daniel Stenberg (12 Jan 2023) + Note USE_RECV_BEFORE_SEND_WORKAROUND is a Windows sockets workaround + that has been disabled by default since b4b6e4f1, due to other bugs. -- curl: output warning at --verbose output for debug-enabled version + Closes https://github.com/curl/curl/pull/10961 - + a libcurl warning in the debug output +- cf-h2-proxy: fix processing ingress to stop too early - Assisted-by: Jay Satiro + - progress ingress stopped too early, causing data + from the underlying filters to not be processed and + report that no tunnel data was available + - this lead to "hangers" where no socket activity was + seen but data rested in buffers - Ref: https://curl.se/mail/lib-2023-01/0039.html - Closes #10278 + Closes #10952 -- src: add --http3-only +- http3: check stream_ctx more thoroughly in all backends - Warning: --http3 and --http3-only are subject to change again (or be - removed) before HTTP/3 support goes non-experimental. + - callbacks and filter methods might be invoked at unexpected + times, e.g. when the transfer's stream_ctx has not been initialized + yet or, more likely, has already been taken down. + - check for existance of stream_ctx in such places and return + an error or silently succeed the call. - Closes #10264 + Closes #10951 -- curl.h: add CURL_HTTP_VERSION_3ONLY +Daniel Stenberg (13 Apr 2023) - As the previous CURL_HTTP_VERSION_3 option gets a slightly altered meaning. +- ftp: fix 'portsock' variable was assigned the same value - Closes #10264 + Pointed out by PVS -- connect: fix access of pointer before NULL check + Ref: #10929 + Closes #10955 - Detected by Coverity CID 1518992 +- ftp: remove dead code - Closes #10284 + This condition can never be true here since it is handled already 28 + lines above. -Daniel Gustafsson (12 Jan 2023) + Pointed out by PVS. -- easyoptions: Fix header printing in generation script + Ref: #10929 + Closes #10957 - The optiontable.pl script prints the header comment when generating - easyoptions.c, but it wasn't escaping all characters which jumbled the - curl ascii logo. Fix by escaping. +- cf-h1-proxy: skip an extra NULL assign - Cloes #10275 + and use Curl_safefree() once to save another NULL assign. Found by PVS. -Harry Sintonen (12 Jan 2023) + Ref. #10929 + Closes #10953 -- tool_getparam: fix hiding of command line secrets +Philip Heiduck (13 Apr 2023) - Closes #10276 +- GHA: suppress git clone output -Stefan Eissing (12 Jan 2023) + Follow-up: https://github.com/curl/curl/commit/8203aa6ed405ec832d2c62f18dfda2 + 93f89a23f9 -- tests: document the cfilter debug logging options + Closes #10949 - Closes #10283 +Stefan Eissing (13 Apr 2023) -- curl_log: for failf/infof and debug logging implementations +- cf-socket: remove dead code discovered by PVS - - new functions and macros for cfilter debugging - - set CURL_DEBUG with names of cfilters where debug logging should be - enabled - - use GNUC __attribute__ to enable printf format checks during compile + Closes #10960 - Closes #10271 +Daniel Stenberg (13 Apr 2023) -Daniel Stenberg (10 Jan 2023) +- http: skip a double NULL assign -- RELEASE-NOTES: synced + and also use a local variable to shorten the long names and increase + readability in the function. Pointed out by PVS. -Nick Banks (10 Jan 2023) + Ref: #10929 + Closes #10950 -- msh3: update to v0.6 +- mime: skip NULL assigns after Curl_safefree() - Closes #10192 + Pointed out by PVS. -Stefan Eissing (10 Jan 2023) + Ref: #10929 + Closes #10947 -- ngtcp2: add CURLOPT_SSL_CTX_FUNCTION support for openssl+wolfssl +- rtsp: skip NULL assigns after Curl_safefree() - Using common method for SSL_CTX initialization of verfiy peer and CA - settings. This also provides X509_STORE sharing to become available for - ngtcp2+openssl HTTP/3. + ... since this is a macro that assigns NULL itself. Pointed out by PVS. - Reported-by: violetlige on github + Ref: #10929 + Closes #10946 - Fixes #10222 - Closes #10239 +- smb: remove double assign -Daniel Stenberg (10 Jan 2023) + The same value is assigned the same value already a few lines above. + Pointed out by PVS. -- cf-socket: make infof() call use %zu for size_t output + Ref: #10929 + Closes #10945 - Detected by Coverity CID 1518986 and CID 1518984 +- transfer: skip extra assign - Closes #10268 + The 'result' variable already contains CURLE_OK at this point, no use in + setting it again. Pointed out by PVS. -Jon Rumsey (10 Jan 2023) + Ref: #10929 + Closes #10944 -- os400: fixes to make-lib.sh and initscript.sh +- urlapi: skip a pointless assign - Adjust how exports list is generated from header files to account for - declarations across multiple lines and CURL_DEPRECATED(...) tags. + It stores a null byte after already having confirmed there is a null + byte there. Detected by PVS. - Update initscript.sh + Ref: #10929 + Closes #10943 - Specify qadrt_use_inline to prevent unistd.h in ASCII runtime defining - close(a) -> close_a(a) +Philip Heiduck (13 Apr 2023) - Fixes #10266 - Closes #10267 +- GHA: suppress git clone output -Stefan Eissing (9 Jan 2023) + Closes #10939 -- tests-httpd: basic infra to run curl against an apache httpd plus nghttpx for - h3 +Stefan Eissing (13 Apr 2023) - - adding '--with-test-httpd=' to configure non-standard apache2 - install - - python env and base classes for running httpd - - basic tests for connectivity with h1/h2/h3 - - adding test cases for truncated responses in http versions. - - adding goaway test for HTTP/3. - - adding "stuttering" tests with parallel downloads in chunks with - varying delays between chunks. +- tests: make test_12_01 a bit more forgiving on connection counts - - adding a curltest module to the httpd server, adding GOAWAY test. - - mod_curltest now installs 2 handlers - - 'echo': writing as response body what came as request body - - 'tweak': with query parameters to tweak response behaviour - - marked known fails as skip for now +- cf-socket: add socket recv buffering for most tcp cases - Closes #10175 + - use bufq as recv buffer, also for Windows pre-receive handling + - catch small reads followed by larger ones in a single socket + call. A common pattern on TLS connections. -- quic: improve connect error message, debugging info, fix false connect report + Closes #10787 - - ECONNECTREFUSED has not its own fail message in quic filters - - Debug logging in connect eyballing improved - - Fix bug in ngtcp2/quiche that could lead to false success reporting. +Daniel Stenberg (13 Apr 2023) - Reported-by: Divy Le Ray +- urlapi: cleanups - Fixes #10245 - Closes #10248 + - move host checks together + - simplify the scheme parser loop and the end of host name parser + - avoid itermediate buffer storing in multiple places + - reduce scope for several variables + - skip the Curl_dyn_tail() call for speed + - detect IPv6 earlier and skip extra checks for such hosts + - normalize directly in dynbuf instead of itermediate buffer + - split out the IPv6 parser into its own funciton + - call the IPv6 parser directly for ipv6 addresses + - remove (unused) special treatment of % in host names + - junkscan() once in the beginning instead of scattered + - make junkscan return error code + - remove unused query management from dedotdotify() + - make Curl_parse_login_details use memchr + - more use of memchr() instead of strchr() and less strlen() calls + - make junkscan check and return the URL length -- quiche: fix build without any HTTP/2 implementation + An optimized build runs one of my benchmark URL parsing programs ~41% + faster using this branch. (compared against the shipped 7.88.1 library + in Debian) - Fixes #10260 - Closes #10263 + Closes #10935 -Daniel Stenberg (9 Jan 2023) +Josh McCullough (13 Apr 2023) -- .github/workflows/linux.yml: add a quiche CI job +- http2: fix typo in infof() call - Move over from zuul + Closes #10940 - Closes #10241 +Daniel Stenberg (12 Apr 2023) -- curl.h: allow up to 10M buffer size +- noproxy: pointer to local array 'hostip' is stored outside scope - Bump the limit from 512K. There might be reasons for applications using - h3 to set larger buffers and there is no strong reason for curl to have - a very small maximum. + Ref: #10929 + Closes #10933 - Ref: https://curl.se/mail/lib-2023-01/0026.html +Stefan Eissing (12 Apr 2023) - Closes #10256 +- connect: fix https connection setup to treat ssl_mode correctly -Tatsuhiro Tsujikawa (8 Jan 2023) + - for HTTPS protocol, a disabled ssl should never be acceptables. -- GHA: use designated ngtcp2 and its dependencies versions + Closes #10934 - Designate ngtcp2 and its dependency versions so that the CI build does - not fail without our control. +Douglas R. Reno (12 Apr 2023) - Closes #10257 +- CMakeLists.txt: fix typo for Haiku detection -Daniel Stenberg (8 Jan 2023) + Closes #10937 -- docs/cmdline-opts/hsts.d: explain hsts more +Dan Fandrich (11 Apr 2023) - Closes #10258 +- pathhelp: use the cached $use_cygpath when available -Stefan Eissing (8 Jan 2023) +- runtests: eliminate unneeded variable -- msh3: run again in its cfilter +- runtests: make the # of server start attempts a constant - - test 2500, single GET works - - test 2501, single POST stalls - - test 2502, multiple, sequential GETs each use a new connection since - MsH3ConnectionGetState(qconn) no longer reports CONNECTED after one - GET. +- runtests: on startup failure call displaylogs only in serverfortest - Closes #10204 + This reduces the number of calls spread throughout the code. -Jay Satiro (8 Jan 2023) + Ref: #10818 + Closes #10919 -- sendf: fix build for Linux TCP fastopen +- runtests: return an error code with startservers() - - Fix the remote addr struct dereference. + The code indicates the kind of failure encountered in starting a server, + which can be used by the caller to tailor the user experience. - - Include cf-socket.h in urldata.h. + Ref: #10818 - Follow-up to 6a8d7ef9 which changed conn->ipaddr (Curl_addrinfo* ) - member to conn->remote_addr (Curl_sockaddr_ex *) several days ago. +- runtests: abort early if runpingpongserver is given a bad server type - Reported-by: Stephan Guilloux +- runtests: don't use the SMB server verification time as reference - Fixes https://github.com/curl/curl/issues/10249 - Closes https://github.com/curl/curl/pull/10250 + %FTPTIME2 and %FTPTIME3 should be set by the FTP server only, for + consistency. -Daniel Stenberg (7 Jan 2023) +- tests: factor out the test server management code -- RELEASE-NOTES: synced + This now lives in servers.pm with some configuration variables moved to + globalconfig.pm -- setopt: move the SHA256 opt within #ifdef libssh2 + Ref: #10818 - Because only the libssh2 backend not supports it and thus this should - return error if this option is used other backends. +- runtests: remove an inappropriate use of runclientoutput - Reported-by: Harry Sintonen + This function is intended for running client code, not servers. - Closes #10255 +- runtests: only add $LIBDIR to the path for checktestcmd -Patrick Monnerat (7 Jan 2023) + Since checkcmd is for finding servers, there will never be anything in + this directory of interest to them. -- nss: implement data_pending method + Ref: #10818 - NSS currently uses the default Curl_none_data_pending() method which - always returns false, causing TLS buffered input data to be missed. +- tests: log sshserver.pl messages to a file - The current commit implements the nss_data_pending() method that properly - monitors the presence of available TLS data. + The logmsg messages were thrown away before, so they are now available + for debugging. - Ref:#10077 +- runtests: also show DISABLED tests with -l - Closes #10225 + Other reasons for skipping tests are ignored for -l, so being explicitly + disabled should be too. -Jay Satiro (6 Jan 2023) +- runtests: move the UNIX sockets into $PIDDIR -- CURLOPT_HEADERDATA.3: warn DLL users must set write function + These were missed when the other server files were moved there. - - Warn that in Windows if libcurl is running from a DLL and if - CURLOPT_HEADERDATA is set then CURLOPT_WRITEFUNCTION or - CURLOPT_HEADERFUNCTION must be set as well, otherwise the user may - experience crashes. + Follow-up to 70d2fca2 - We already have a similar warning in CURLOPT_WRITEDATA. Basically, in - Windows libcurl could crash writing a FILE pointer that was created by - a different C runtime. In Windows each DLL that is part of a program may - or may not have its own C runtime. + Ref: #10818 - Ref: https://github.com/curl/curl/issues/10231 +- tests: tighten up perl exports - Closes https://github.com/curl/curl/pull/10233 + This reduces namespace pollution a little. -Jon Rumsey (5 Jan 2023) + Ref: #10818 -- x509asn1: fix compile errors and warnings +- tests: turn perl modules into full packages - Various small issues when built for GSKit + This helps enforce more modularization and encapsulation. Enable and fix + warnings on a few packages. Also, rename ftp.pm to processhelp.pm since + there's really nothing ftp-specific in it. - Closes #10238 + Ref: #10818 -Patrick Monnerat (5 Jan 2023) +Daniel Stenberg (11 Apr 2023) -- runtests: fix detection of TLS backends +- multi: remove a few superfluous assigns - Built-in TLS backends are detected at test time by scanning for their - names in the version string line returned by the cli tool: as this line - may also list the libssh configuration that mentions its own backend, - the curl backend may be wrongly determined. + PVS found these "The 'rc' variable was assigned the same value." cases. - In example, if the version line contains "libssh/0.10.4/openssl/zlib", - OpenSSL is detected as a curl-configured backend even if not. + Ref: #10929 + Closes #10932 - This fix requires the backend names to appear as full words preceded by - spacing in the version line to be recognized as curl TLS backends. +- schannel: add clarifying comment - Closes #10236 + Explaining how the PVS warning in #10929 is wrong: Dereferencing of the + null pointer 'backend->cred' might take place. -Andy Alt (5 Jan 2023) + Closes #10931 -- GHA: add job on Slackware 15.0 +- cookie: clarify that init with data set to NULL reads no file - Closes #10230 + ... and make Curl_cookie_add() require 'data' being set proper with an + assert. -Daniel Stenberg (5 Jan 2023) + The function has not worked with a NULL data for quite some time so this + just corrects the code and comment. -- test363: make even smaller writes to loop more + This is a different take than the proposed fixed in #10927 -- http_proxy: do not assign data->req.p.http use local copy + Reported-by: Kvarec Lezki + Ref: #10929 + Closes #10930 - Avoid the tricky reusing of the data->req.p.http pointer for http proxy - tunneling. +Kvarec Lezki (11 Apr 2023) - Fixes #10194 - Closes #10234 +- vtls: remove int typecast for sizeof() -Stefan Eissing (5 Jan 2023) + V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> + memsize. The value being cast: 'sizeof + (buf->data)'. curl\lib\vtls\vtls.c 2025 -- quic: rename vquic implementations, fix for quiche build. + https://pvs-studio.com/en/docs/warnings/v220/ - - quiche in debug mode did not build, fixed. - - moved all vquic implementation files to prefix curl_* to avoid - the potential mixups between provided .h files and our own. - - quich passes test 2500 and 2502. 2501, the POST, fail with - the body being rejected. Quich bug? + Closes #10928 - Closes #10242 +Stefan Eissing (11 Apr 2023) -- sectransp: fix for incomplete read/writes +- http2: fix copynpaste error reported by coverity - SecureTransport expects result code errSSLWouldBlock when the requested - length could not be sent/recieved in full. The previous code returned - noErr, which let SecureTransport to believe that the IO had terminated - prematurely. + - move all code handling HTTP/2 frames for a particular + stream into a separate function to keep from confusing + the call `data` with the stream `data`. - Fixes #10227 - Closes #10235 + Closes #10924 -Andy Alt (5 Jan 2023) +Dan Fandrich (11 Apr 2023) -- GHA: Hacktoberfest CI: Update deprecated 'set-output' command +- tests: log a too-long Unix socket path in sws and socksd - Closes #10221 + Ref: #10919 -Jay Satiro (5 Jan 2023) +Daniel Stenberg (11 Apr 2023) -- scripts: set file mode +x on all perl and shell scripts +- gen.pl: error on duplicated See-Also fields - - Set all scripts +x, ie 644 => 755. + Updated http2.d accordingly. - Prior to this change some scripts were not executable and therefore - could not be called directly. + Closes #10925 - ~~~ - git ls-files -s \*.{sh,pl,py} | grep -v 100755 - ~~~ +- http2: avoid possible null pointer dereference - Closes https://github.com/curl/curl/pull/10219 + Reported-by: Dan Fandrich + Fixes #10920 + Closes #10923 -Stefan Eissing (4 Jan 2023) +- lib1560: verify that more bad host names are rejected -- tool_operate: fix headerfile writing + when setting the hostname component of a URL - Do not rely on the first transfer started to be the first to get a - response (remember -Z). All transfers now write the headefile (-D) in - append mode, making sure that the order of transfer responses does not - lead to overwrites of previous data. + Closes #10922 - Closes #10224 +- curl_url_set.3: mention that users can set content rather freely -Daniel Stenberg (4 Jan 2023) + ... which then might render bad URLs if you extract a URL later. -- misc: reduce struct and struct field sizes + Closes #10921 - - by using BIT() instead of bool - - imap: shrink struct - - ftp: make state 'unsigned char' - - ftp: sort ftp_conn struct entries on size - - urldata: use smaller fields for SSL version info storage - - pop3: reduce the pop3_conn struct size - - smtp: reduce the size of the smtp structs +Dan Fandrich (10 Apr 2023) - Closes #10186 +- CI: retry failed downloads of aws-lc -- noproxy: support for space-separated names is deprecated + Don't fail the build in case of a temporary server problem. - To be removed in July 2024. +- test1169: fix so it works properly everywhere - Assisted-by: Michael Osipov - Fixes #10209 - Closes #10215 + - Use an absolute path for the -L option since the module isn't in the + perl path + - Create the needed test file in a section; isn't + intended for this + - Fix the test number in the file name, which was wrong -Andrei Rybak (4 Jan 2023) + Follow-up to f754990a -- lib: fix typos in comments which repeat a word + Ref: #10818 + Fixes #10889 + Closes #10917 - Remove erroneously duplicated words in code comments of files - `lib.connect.c` and `lib/url.c`. +- tests: stop using strndup(), which isn't portable - Closes #10220 + It's not available on Solaris 10, for example. Since this is just test + code that doesn't need to use an optimized system version, replace it + with the implementation copied from tool_cb_hdr.c. -Radek Brich (3 Jan 2023) +- runtests: fix an incorrect comment about the ld_preload feature -- cmake: set SOVERSION also for macOS + Follow-up to 1f631864 - Closes #10214 + Ref: #10818 -Jay Satiro (3 Jan 2023) +Daniel Stenberg (9 Apr 2023) -- http2: fix compiler warning due to uninitialized variable +- urlapi: prevent setting invalid schemes with *url_set() - Prior to this change http2_cfilter_add could return an uninitialized - cfilter pointer in an OOM condition. In this case though, the pointer - is discarded and not dereferenced so there was no risk of a crash. + A typical mistake would be to try to set "https://" - including the + separator - this is now rejected as that would then lead to + url_get(... URL...) would get an invalid URL extracted. -Stefan Eissing (3 Jan 2023) + Extended test 1560 to verify. -- cf-socket: keep sockaddr local in the socket filters + Closes #10911 - - copy `struct Curl_addrinfo` on filter setup into context - - remove `struct Curl_addrinfoi *` with `struct Curl_sockaddr_ex *` in - connectdata that is set and NULLed by the socket filter - - this means we have no reference to the resolver info in connectdata or - its filters - - trigger the CF_CTRL_CONN_INFO_UPDATE event when the complete filter - chain reaches connected status - - update easy handle connection information on CF_CTRL_DATA_SETUP event. +Biswapriyo Nath (9 Apr 2023) - Closes #10213 +- http2: remove unused Curl_http2_strerror function declaration -Daniel Stenberg (3 Jan 2023) + Curl_http2_strerror was renamed to http2_strerror in + 05b100aee247bb9bec8e9a1b0 and then http2_strerror was removed in + 5808a0d0f5ea0399d4a2a2 -- RELEASE-NOTES: synced + This also fixes the following compiler error -- runtests: consider warnings fatal and error on them + lib/http2.h:41:33: error: unknown type name 'uint32_t' + lib/http2.h:1:1: note: 'uint32_t' is defined in header '' - To help us detect and fix warnings in this script easier and faster. + Closes #10912 - Assisted-by: Jakob Hirsch +Daniel Stenberg (8 Apr 2023) - Ref: #10206 - Closes #10208 +- RELEASE-NOTES: synced -- copyright: update all copyright lines and remove year ranges +SuperIlu on github (8 Apr 2023) - - they are mostly pointless in all major jurisdictions - - many big corporations and projects already don't use them - - saves us from pointless churn - - git keeps history for us - - the year range is kept in COPYING +- config-dos.h: fix SIZEOF_CURL_OFF_T for MS-DOS/DJGPP - checksrc is updated to allow non-year using copyright statements + Fixes #10905 + Closes #10910 - Closes #10205 +Daniel Stenberg (8 Apr 2023) -- docs/DEPRECATE.md: deprecate gskit +- lib: remove CURLX_NO_MEMORY_CALLBACKS - Ref: #10163 + The only user of this define was 'chkdecimalpoint' - a special purpose + test tool that was built but not used anymore (since 17c18fbc3 - Apr + 2020). - - This is a niche TLS library, only running on some IBM systems - - no regular curl contributors use this backend - - no CI builds use or verify this backend - - gskit, or the curl adaption for it, lacks many modern TLS features - making it an inferior solution - - build breakages in this code take weeks or more to get detected - - fixing gskit code is mostly done "flying blind" + Closes #10908 - Closes #10201 +- CURLPROXY_HTTPS2: for HTTPS proxy that may speak HTTP/2 -- Revert "x509asn1: avoid freeing unallocated pointers" + Setting this proxy type allows curl to negotiate and use HTTP/2 with + HTTPS proxies. - This reverts commit 6b19247e794cfdf4ec63c5880d8f4f5485f653ab. + Closes #10900 - Fixes #10163 - Closes #10207 +Ali Khodkar (8 Apr 2023) -- ngtcp2: fix the build without 'sendmsg' +- write-out.d: add missing periods - Follow-up from 71b7e0161032 + Closes #10897 - Closes #10210 +Daniel Stenberg (7 Apr 2023) -- cmake: check for sendmsg +- http2: remove check for !data after it was already dereferenced - Used by ngtcp2 + Pointed out by Coverity - Closes #10211 + Closes #10906 -Timmy Schierling (2 Jan 2023) +- http_proxy: provide missing arg to infof() call -- runtest.pl: add expected fourth return value + Pointed out by Coverity - Fixes warning in autobild log: "Use of uninitialized value $HTTP2TLSPORT - in substitution iterator at /tests/runtests.pl line 3516" + Closes #10904 - Closes #10206 +- content_encoding: only do tranfer-encoding compression if asked to -Daniel Stenberg (2 Jan 2023) + To reduce surprises. Update test 387 and 418 accordingly. -- http2: when using printf %.*s, the length arg must be 'int' + Closes #10899 - Detected by Coverity CID 1518341 +- sws: comparison of unsigned expression < 0 is always false - Closes #10203 + Follow-up to 356dd0b73a75ed6d5 -- cfilters: check for NULL before using pointer + Closes #10903 - Detected by Coverity CID 1518343 +- lib/cmake: add HAVE_WRITABLE_ARGV check - Closes #10202 + Assisted-by: Jakub Zakrzewski + Closes #10896 -- http2: in connisdead check, attach the connection before reading +- configure: don't set HAVE_WRITABLE_ARGV on Windows - Otherwise data->conn is NULL and things go wrong. + Ref: #10888 + Closes #10896 - This problem caused occastional failures in test 359, 1700 and more - depending on timing and the alignment of various planets. +- vtls: fix build error when proxy-disabled - Assisted-by: Stefan Eissing + Closes #10901 - Closes #10199 +Stefan Eissing (6 Apr 2023) -Philip Heiduck (2 Jan 2023) +- tests: increase sws timeout for more robust testing -- Linux CI: update some dependecies to latest tag + - for https CONNECT forwarding, this was fixed at 5 seconds + which led to spurious CI test failures + - add --keepalive parameter to sws to control this + - let httpserver use 30 seconds - Closes #10195 + Closes #10898 -Daniel Stenberg (2 Jan 2023) +- http2: move HTTP/2 stream vars into local context -- c-hyper: move down the Accept-Encoding header generation + - remove NGHTTP2 members of `struct HTTP` + - add `void *h2_ctx` to `struct HTTP` + - add `void *h3_ctx` to `struct HTTP` + - separate h2/h3 pointers are needed for eyeballing + - manage local stream_ctx in http implementations - To match the internal HTTP request header order so that test 1277 works - again. + Closes #10877 - Closes #10200 +- proxy: http2 proxy tunnel implementation -- release-notes.pl: check fixes/closes lines better + - currently only on debug build and when env variable + CURL_PROXY_TUNNEL_H2 is present. + - will ALPN negotiate with the proxy server and switch + tunnel filter based on the protocol negotiated. + - http/1.1 tunnel code moved into cf-h1-proxy.[ch] + - http/2 tunnel code implemented in cf-h2-proxy.[ch] + - tunnel start and ALPN set remains in http_proxy.c + - moving all haproxy related code into cf-haproxy.[ch] - To better skip lines that just happen to mention those words at the - start of a line without being instructions. + VTLS changes + - SSL filters rely solely on the "alpn" specification they + are created with and no longer check conn->bits.tls_enable_alpn. + - checks on which ALPN specification to use (or none at all) are + done in vtls.c when creating the filter. -- test1560: use a UTF8-using locale when run + Testing + - added a nghttpx forward proxy to the pytest setup that + speaks HTTP/2 and forwards all requests to the Apache httpd + forward proxy server. + - extending test coverage in test_10 cases + - adding proxy tests for direct/tunnel h1/h2 use of basic auth. + - adding test for http/1.1 and h2 proxy tunneling to pytest - There are odd cases that don't use UTF8 and then the IDN handling goes - wrong. + Closes #10780 - Reported-by: Marcel Raad - Fixes #10193 - Closes #10196 +- vtls and h2 improvements -- cf-socket: fix build regression + - eliminate receive loop in vtls to fill buffer. This may + lead to partial reads of data which is counter productive + - let http2 instead loop smarter to process pending network + data without transfer switches - Reported-by: Stephan Guilloux - Fixes #10190 - Closes #10191 + scorecard improvements + - do not start caddy when only httpd is requested + - allow curl -v to stderr file on --curl-verbose -- examples: remove the curlgtk.c example + Closes #10891 - - it does not add a lot of value - - we do not test-build it to verify because of its dependencies - - unclear for what GTK versions it works or not +Daniel Stenberg (6 Apr 2023) - Reported-by: odek86 on github +- tests: 1078 1288 1297 use valid IPv4 addresses - Fixes #10197 - Closes #10198 + With the enhanced URL parser, these tests failed because of their bad + IPv4 use. -Andy Alt (2 Jan 2023) +- urlapi: detect and error on illegal IPv4 addresses -- docs: add link to GitHub Discussions + Using bad numbers in an IPv4 numerical address now returns + CURLUE_BAD_HOSTNAME. - Closes #10171 + I noticed while working on trurl and it was originally reported here: + https://github.com/curl/trurl/issues/78 -- GHA: ignore changes to md files for most workflows + Updated test 1560 accordingly. - Closes #10176 + Closes #10894 -Josh Brobst (2 Jan 2023) +- RELEASE-NOTES: synced -- http: decode transfer encoding first +- urlapi: URL encoding for the URL missed the fragment - The unencoding stack is added to as Transfer-Encoding and - Content-Encoding fields are encountered with no distinction between the - two, meaning the stack will be incorrect if, e.g., the message has both - fields and a non-chunked Transfer-Encoding comes first. This commit - fixes this by ordering the stack with transfer encodings first. + Meaning that it would wrongly still store the fragment using spaces + instead of %20 if allowing space while also asking for URL encoding. - Reviewed-by: Patrick Monnerat - Closes #10187 + Discovered when playing with trurl. -Daniel Stenberg (1 Jan 2023) + Added test to lib1560 to verify the fix. -- curl.h: mark CURLSSLBACKEND_MESALINK as deprecated + Closes #10887 - Follow-up since 223f26c28a340b36 +- rtsp: convert mallocs to dynbuf for RTP buffering - Deprecated since 7.82.0 + Closes #10786 - Closes #10189 +- tool_writeout: add URL component variables -- curl_global_sslset.3: clarify the openssl situation + Output specific components from the used URL. The following variables + are added for this purpose: - and add rustls + url.scheme, url.user, url.password, url.options, url.host, url.port, + url.path, url.query, url.fragment, url.zoneid - Closes #10188 + Add the following for outputting parts of the "effective URL": -Cameron Blomquist (1 Jan 2023) - -- http: add additional condition for including stdint.h - - stdint.h was only included in http.h when ENABLE_QUIC was defined, but - symbols from stdint.h are also used when USE_NGHTTP2 is defined. This - causes build errors when USE_NGHTTP2 is defined but ENABLE_QUIC is not. - - Closes #10185 - -Daniel Stenberg (31 Dec 2022) - -- urldata: cease storing TLS auth type - - The only TLS auth type libcurl ever supported is SRP and that is the - default type. Since nobody ever sets any other type, there is no point - in wasting space to store the set type and code to check the type. - - If TLS auth is used, SRP is now implied. - - Closes #10181 - -- vtls: use ALPN HTTP/1.0 when HTTP/1.0 is used - - Previously libcurl would use the HTTP/1.1 ALPN id even when the - application specified HTTP/1.0. - - Reported-by: William Tang - Ref: #10183 - -Marcel Raad (30 Dec 2022) - -- lib670: make test.h the first include - - As in all other lib tests. This avoids a macro redefinition warning for - `_FILE_OFFSET_BITS` visible in the autobuilds. - - Closes https://github.com/curl/curl/pull/10182 - -Stefan Eissing (30 Dec 2022) - -- lib: connect/h2/h3 refactor - - Refactoring of connection setup and happy eyeballing. Move - nghttp2. ngtcp2, quiche and msh3 into connection filters. - - - eyeballing cfilter that uses sub-filters for performing parallel connects - - socket cfilter for all transport types, including QUIC - - QUIC implementations in cfilter, can now participate in eyeballing - - connection setup is more dynamic in order to adapt to what filter did - really connect. Relevant to see if a SSL filter needs to be added or - if SSL has already been provided - - HTTP/3 test cases similar to HTTP/2 - - multiuse of parallel transfers for HTTP/3, tested for ngtcp2 and quiche - - - Fix for data attach/detach in VTLS filters that could lead to crashes - during parallel transfers. - - Eliminating setup() methods in cfilters, no longer needed. - - Improving Curl_conn_is_alive() to replace Curl_connalive() and - integrated ssl alive checks into cfilter. - - Adding CF_CNTRL_CONN_INFO_UPDATE to tell filters to update - connection into and persist it at the easy handle. - - - Several more cfilter related cleanups and moves: - - stream_weigth and dependency info is now wrapped in struct - Curl_data_priority - - Curl_data_priority members depend is available in HTTP2|HTTP3 - - Curl_data_priority members depend on NGHTTP2 support - - handling init/reset/cleanup of priority part of url.c - - data->state.priority same struct, but shallow copy for compares only - - - PROTOPT_STREAM has been removed - - Curl_conn_is_mulitplex() now available to check on capability - - - Adding query method to connection filters. - - ngtcp2+quiche: implementing query for max concurrent transfers. - - - Adding is_alive and keep_alive cfilter methods. Adding DATA_SETUP event. - - setting keepalive timestamp on connect - - DATA_SETUP is called after the connection has been completely - setup (but may not connected yet) to allow filters to initialize - data members they use. - - - there is no socket to be had with msh3, it is unclear how select - shall work - - - manual test via "curl --http3 https://curl.se" fail with "empty - reply from server". - - - Various socket/conn related cleanups: - - Curl_socket is now Curl_socket_open and in cf-socket.c - - Curl_closesocket is now Curl_socket_close and in cf-socket.c - - Curl_ssl_use has been replaced with Cur_conn_is_ssl - - Curl_conn_tcp_accepted_set has been split into - Curl_conn_tcp_listen_set and Curl_conn_tcp_accepted_set - with a clearer purpose - - Closes #10141 - -Daniel Stenberg (30 Dec 2022) - -- RELEASE-NOTES: synced - -- docs/libcurl/curl_getdate.3: minor whitespace edit - - To avoid a fccp quirk that made it render wrongly on the website - -- transfer: break the read loop when RECV is cleared - - When the RECV bit is cleared because the response reading for this - transfer is complete, the read loop should be stopped. data_pending() - can otherwise still return TRUE and another read would be attempted. - - Reported-by: Hide Ishikawa - Fixes #10172 - Closes #10174 - -- multihandle: turn bool struct fields into bits - - Closes #10179 - -Stefan Eissing (30 Dec 2022) - -- ftpserver: lower the normal DATA connect timeout to speed up torture tests - - - tests/ftpserver.pl blocks when expecting a DATA connection from the - client. - - - the previous 10 seconds were encountered repeatedly in torture tests - and let to long waits. - - - 2 seconds should still be sufficient for current hw, but CI will show. - - Closes #10178 - -Nick Banks (28 Dec 2022) - -- msh3: add support for request payload - - Closes #10136 - -Stefan Eissing (28 Dec 2022) - -- openssl: remove attached easy handles from SSL instances - - - keeping the "current" easy handle registered at SSL* is no longer - necessary, since the "calling" data object is already stored in the - cfilter's context (and used by other SSL backends from there). - - The "detach" of an easy handle that goes out of scope is then avoided. - - using SSL_set0_wbio for clear reference counting where available. - - Closes #10151 - -Daniel Stenberg (28 Dec 2022) - -- socketpair: allow localhost MITM sniffers - - Windows allow programs to MITM connections to localhost. The previous - check here would detect that and error out. This new method writes data - to verify the pipe thus allowing MITM. - - Reported-by: SerusDev on github - Fixes #10144 - Closes #10169 - -- HTTP3: mention what needs to be in place to remove EXPERIMENTAL label - - Closes #10168 - -Andy Alt (28 Dec 2022) - -- MANUAL.md: add pipe to apt-key example - - Closes #10170 - -Daniel Stenberg (27 Dec 2022) - -- test417: verify %{certs} output - -- runtests: make 'mbedtls' a testable feature - - Also add to FILEFORMAT.md - -- writeout: add %{certs} and %{num_certs} - - Let users get the server certificate chain using the command line - - Closes #10019 - -Stefan Eissing (27 Dec 2022) - -- haxproxy: send before TLS handhshake - - - reverse order of haproxy and final ssl cfilter - - - make haproxy avaiable on PROXY builds, independent of HTTP support as - it can be used with any protocol. - - Reported-by: Sergio-IME on github - Fixes #10165 - Closes #10167 - -Daniel Stenberg (27 Dec 2022) - -- RELEASE-NOTES: synced - -- test446: verify hsts with two URLs - -- runtests: support crlf="yes" for verify/proxy - -- hsts: handle adding the same host name again - - It will then use the largest expire time of the two entries. - -- tool_operate: share HSTS between handles - -- share: add sharing of HSTS cache among handles - - Closes #10138 - -Viktor Szakats (27 Dec 2022) - -- Makefile.mk: fix wolfssl and mbedtls default paths - - Fix the defaults for `WOLFSSL_PATH` and `MBEDTLS_PATH` to have - meaningful values instead of the copy-pasted wrong ones. - - Ref: https://github.com/curl/curl/commit/66e68ca47f7fd00dff2cb7c45ba6725d4009 - 9585#r94275172 - - Reported-by: Ryan Schmidt - Closes #10164 - -Daniel Stenberg (27 Dec 2022) - -- INTERNALS: cleanup - - - remove "operating systems" (mostly outdated) - - - upodate the "build tools" - - Closes #10162 - -- cmake: bump requirement to 3.7 - - Because this is the cmake version (released in November 2016) that - introduced GREATER_EQUAL, which is used already. - - Reported-by: nick-telia on github - Fixes #10128 - Closes #10161 - -- cfilters:Curl_conn_get_select_socks: use the first non-connected filter - - When there are filters addded for both socket and SSL, the code - previously checked the SSL sockets during connect when it *should* first - check the socket layer until that has connected. - - Fixes #10157 - Fixes #10146 - Closes #10160 + urle.scheme, urle.user, urle.password, urle.options, urle.host, urle.port, + urle.path, urle.query, urle.fragment, urle.zoneid - Reviewed-by: Stefan Eissing + Added test 423 and 424 to verify. -- urlapi: add CURLU_PUNYCODE + Closes #10853 - Allows curl_url_get() get the punycode version of host names for the - host name and URL parts. +Stefan Eissing (4 Apr 2023) - Extend test 1560 to verify. +- tests/http: improved httpd detection - Closes #10109 + - better error messages when not found/complete + - handling of `--without-test-httpd` -- RELEASE-NOTES: synced + Reported-by: kwind on github + Fixes #10879 + Closes #10883 -- libssh2: try sha2 algos for hostkey methods +Daniel Stenberg (4 Apr 2023) - As is supported by recent libssh2, but should just be ignored by older - versions. +- configure: make quiche require quiche_conn_send_ack_eliciting - Reported-by: norbertmm on github - Assisted-by: norbertmm on github - Fixes #10143 - Closes #10145 + curl now requires quiche version >= 1.17.1 to be used and this function + was added in this version and makes a convenient check. -Patrick Monnerat (26 Dec 2022) + This requirement is because this is the lowest quiche version that + supports peer-initiated key updates correctly. -- typecheck: accept expressions for option/info parameters + Closes #10886 - As expressions can have side effects, evaluate only once. +Dan Fandrich (1 Apr 2023) - To enable deprecation reporting only once, get rid of the __typeof__ - use to define the local temporary variable and use the target type - (CURLoption/CURLINFO). This also avoids multiple reports on type - conflicts (if some) by the curlcheck_* macros. +- unit tests: use the unit test infrastructure better - Note that CURLOPT_* and CURLINFO_* symbols may be deprecated, but not - their values: a curl_easy_setopt call with an integer constant as option - will never report a deprecation. + Allow UNITTEST_STOP to return the error code, use the fail & abort + macros to indicate test failure and return success instead of fail if + the unit test can't test anything because of missing features at + compile-time. A couple of tests could never fail because they were + overriding the failure return code. - Reported-by: Thomas Klausner - Fixes #10148 - Closes #10149 +- runtests: strip EOL on precheck output on Windows, too -Paul Howarth (26 Dec 2022) + Precheck failures would show on two lines in the test summary output + otherwise. -- tests: avoid use of sha1 in certificates +- tests: move server config files under the pid dir - The SHA-1 algorithm is deprecated (particularly for security-sensitive - applications) in a variety of OS environments. This already affects - RHEL-9 and derivatives, which are not willing to use certificates using - that algorithm. The fix is to use sha256 instead, which is already used - for most of the other certificates in the test suite. + These files are generated by the test servers and must therefore be + found in the log directory to make them available to only those servers + once multiple test runners are executing in parallel. They must also not + be deleted with the log files, so they are stored in the pidfile + directory. - Fixes #10135 + Ref: #10818 + Closes #10875 - This gets rid of issues related to sha1 signatures. +- runtests: use the ssh key filenames from the sshhelp package - Manual steps after "make clean-certs" and "make build-certs": +- tests: move pidfiles and portfiles under the log directory - - Copy tests/certs/stunnel-sv.pem to tests/stunnel.pem - (make clean-certs does not remove the original tests/stunnel.pem) + This is to segregate all files written by a test process into a single + root to allow for future parallel testing. - - Copy tests/certs/Server-localhost-sv.pubkey-pinned into --pinnedpubkey - options of tests/data/test2041 and tests/data/test2087 + Ref: #10818 + Closes #10874 - Closes #10153 +- runtests: minor code cleanups -Yurii Rashkovskii (26 Dec 2022) +- runtests: call processexists() and pidfromfile() -- cmake: fix the snprintf detection + rather than duplicating the logic in several places. - I haven't had the time to check other configurations, but on my macOS - Ventura 13.1 with XCode 14.2 cmake does not find `snprintf`. +Viktor Szakats (31 Mar 2023) - Solution: ensure stdio.h is checked for definitions +- cmake: do not add zlib headers for openssl - Closes #10155 + Logic copied earlier from wolfSSL. wolfSSL requires zlib headers for its + public headers. OpenSSL does not, so stop adding zlib headers for it. -Radu Hociung (26 Dec 2022) + Follow-up to 1e3319a167d2f32d295603167486e9e88af9bb4e -- http: remove the trace message "Mark bundle... multiuse" + Closes #10878 - The message "Mark bundle as not supporting multiuse" was added at commit - 29364d93 when an http/2-related bug was fixed, and it appears to be a - leftover trace message. +Stefan Eissing (31 Mar 2023) - This message should be removed because: - * it conveys no information to the user - * it is enabled in the default build (--enable-verbose) - * it reads like a warning/unexpected condition - * it is equivalent to "Detected http proto < 2", which is - not a useful message. - * it is a time-wasting red-herring for anyone who encounters - it for the first time while investigating some other, real - problem. +- rustls: fix error in recv handling - This commit removes the trace message "Mark bundle as not - supporting multiuse" + - when rustls is told to recieve more TLS data and its internal + plaintext buffers are full, it returns an IOERROR + - avoid receiving TLS data while plaintext is not read empty - Closes #10159 + pytest: + - increase curl run timeout when invoking pytest with higher verbosity -Hannah Schierling (26 Dec 2022) + Closes #10876 -- url: fix build with `--disable-cookies` +- http3: improvements across backends - Struct `UserDefined` has no member `cookielist` if - `CURL_DISABLE_COOKIES` is defined. + - ngtcp2: using bufq for recv stream data + - internal stream_ctx instead of `struct HTTP` members + for quiche, ngtcp2 and msh3 + - no more QUIC related members in `struct HTTP` + - experimental use of recvmmsg(), disabled by default + - testing on my old debian box shows no throughput improvements. + - leaving it in, but disabled, for future revisit + - vquic: common UDP receive code for ngtcp2 and quiche + - vquic: common UDP send code for ngtcp2 and quiche + - added pytest skips for known msh3 failures + - fix unit2601 to survive torture testing + - quiche: using latest `master` from quiche and enabling large download + tests, now that key change is supported + - fixing test_07_21 where retry handling of starting a stream + was faulty + - msh3: use bufq for recv buffering headers and data + - msh3: replace fprintf debug logging with LOG_CF where possible + - msh3: force QUIC expire timers on recv/send to have more than + 1 request per second served - Follow-up to af5999a + Closes #10772 - Closes #10158 +Dan Fandrich (30 Mar 2023) -Stefan Eissing (23 Dec 2022) +- test1471/2: add http as a required feature -- runtests: also tear down http2/http3 servers when https server is stopped + curl bails out early with a different error message if http support is + compiled out. - Closes #10114 + Ref: #10705 -- tests: add 3 new HTTP/2 test cases, plus https: support for nghttpx +- tests: limit return code of unit tests and lib tests - - a simple https get - - a simple https post - - a multi get of 4 requests and check that same connection was used + Values greater than 125 have special meanings, so cap it there. Unit + tests and lib tests use the number of failures as the return code, so a + large number of failures (such as test 2601 as a torture test) can + exceed this causing the test to be erroneously reported as having + failed. - Closes #10114 + Ref: #10720 -Daniel Stenberg (23 Dec 2022) +- test1960: point to the correct path for the precheck tool -- urldata: remove unused struct fields, made more conditional + Otherwise, it might find the binary in .libs which can cause it to use + the system libcurl which can fail. This error is only visible by + noticing that the test is skipped. - - source_quote, source_prequote and source_postquote have not been used since - 5e0d9aea3; September 2006 + Follow-up to e4dfe6fc - - make several fields conditional on proxy support + Ref: #10651 - - make three quote struct fields conditional on FTP || SSH +- tests: use the proper %LOGDIR path on two tests - - make 'mime_options' depend on MIME + Follow-up to e7a021e1 - - make trailer_* fields depend on HTTP + Ref: #10818 - - change 'gssapi_delegation' from long to unsigned char +Daniel Stenberg (30 Mar 2023) - - make 'localportrange' unsigned short instead of int +- rtsp: fix Value stored to 'skip_size' is never read - - conn->trailer now depends on HTTP + Pointed out by scan-build - Closes #10147 + Follow-up to 6c6306f3008f2c9b20a64 -- urldata: make set.http200aliases conditional on HTTP being present + Closes #10872 - And make a few SSH-only fields depend on SSH +Stefan Eissing (30 Mar 2023) - Closes #10140 +- tests/http: relax connection check in test_07_02 -- md4: fix build with GnuTLS + OpenSSL v1 + Only 1 connection will be used when curl is slow, happens when + address-sanitized in CI, for example - Reported-by: Esdras de Morais da Silva + Closes #10865 - Fixes #10110 - Closes #10142 +- http2: flow control and buffer improvements -- urldata: make 'ftp_create_missing_dirs' depend on FTP || SFTP + - use bufq for send/receive of network data + - usd bufq for send/receive of stream data + - use HTTP/2 flow control with no-auto updates to control the + amount of data we are buffering for a stream + HTTP/2 stream window set to 128K after local tests, defined + code constant for now + - elminiating PAUSEing nghttp2 processing when receiving data + since a stream can now take in all DATA nghttp2 forwards - Closes #10139 + Improved scorecard and adjuste http2 stream window sizes + - scorecard improved output formatting and options default + - scorecard now also benchmarks small requests / second -John Bampton (22 Dec 2022) + Closes #10771 -- misc: fix grammar and spelling +Dan Fandrich (30 Mar 2023) - Closes #10137 +- runtests: show error message if file can't be written -Daniel Stenberg (22 Dec 2022) +- tests: fix remaining servers to run with a dynamic log directory -- urldata: move the cookefilelist to the 'set' struct + This final commit in the series is sufficient to allow the tests succeed + if $LOGDIR is changed in runtests.pl. - The cookiefile entries are set into the handle and should remain set for - the lifetime of the handle so that duplicating it also duplicates the - list. Therefore, the struct field is moved from 'state' to 'set'. + Ref: #10818 + Closes #10866 - Fixes #10133 - Closes #10134 +- tests: fix fake_ntlm to run with a dynamic log directory -- strdup: name it Curl_strdup + Ref: #10818 - It does not belong in the curlx_ name space as it is never used - externally. +- tests: fix http servers to run with a dynamic log directory - Closes #10132 + Ref: #10818 -Nick Banks (22 Dec 2022) +- tests: fix ftpserver to run with a dynamic log directory -- msh3: update to v0.5 Release + Ref: #10818 - Closes #10125 +- tests: fix C servers to run with a dynamic log directory -Andy Alt (22 Dec 2022) + Ref: #10818 -- workflows/linux.yml: merge 3 common packages +- tests: fix lib tests to run with a dynamic log directory - Closes #10071 + Ref: #10818 -Daniel Stenberg (21 Dec 2022) +- tests: fix unit tests to run with a dynamic log directory -- docs: mention indirect effects of --insecure + Ref: #10818 - Warn users that disabling certficate verification allows servers to - "pollute" curl with data it trusts. +- tests: use %LOGDIR to refer to the log directory - Reported-by: Harry Sintonen - Closes #10126 + This will allow it be set dynamically. -- SECURITY-PROCESS.md: document severity levels + Ref: #10818 - Closes #10118 +- runtests: track verification time even if no files to compare -- RELEASE_NOTES: synced +- getpart: better handle case of file not found - bumped version for new cycle +- testcurl: bump version date -Marcel Raad (21 Dec 2022) + It hadn't been updated in 9 years; it's time. -- tool_operate: fix `CURLOPT_SOCKS5_GSSAPI_NEC` type +- tests: switch to 3-argument open in test suite - `CURLOPT_SOCKS5_GSSAPI_NEC` is a long, while `socks5_gssapi_nec` was - made a bool in commit 4ac64eadf60. + The perl 2-argument open has been considered not-quite-deprecated since + the 3-argument form was introduced almost a quarter century ago. - Closes https://github.com/curl/curl/pull/10124 +- tests: silence some Perl::Critic warnings in test suite -Version 7.87.0 (21 Dec 2022) + Not all warnings are fixed; many are as much stylistic suggestions than + anything and IMHO don't do much to actually improve the code. -Daniel Stenberg (21 Dec 2022) + Ref: #10818 + Closes #10861 -- RELEASE-NOTES: synced +- docs: bump the minimum perl version to 5.6 - The curl 7.87.0 release + It's actually been this way since at least 2012 (when a 3-argument open + was added to runtests.pl). Given the lack of complaints in the interim, + it's safe to call this 23 year old perl version the minimum. -- THANKS: 40 new contributors from 7.87.0 +- runtests: memoize the getpart* subroutines to speed up access -- http: fix the ::1 comparison for IPv6 localhost for cookies + The refactored code calls these functions with the same arguments more + often, so this prevents redundant test case file parsing. - When checking if there is a "secure context", which it is if the - connection is to localhost even if the protocol is HTTP, the comparison - for ::1 was done incorrectly and included brackets. + Approved-by: Daniel Stenberg + Ref: #10818 + Closes #10833 - Reported-by: BratSinot on github +- runtests: remove duplicated feature variables - Fixes #10120 - Closes #10121 + Use the feature map stored in the hash table instead. Most of the + variables were only used only once, to set the value in the hash table. -Philip Heiduck (19 Dec 2022) + Ref: #10818 -- CI/spell: actions/checkout@v2 > actions/checkout@v3 +- runtests: also ignore test file problems when ignoring results -Daniel Stenberg (19 Dec 2022) + This simplifies error handling in the test verification code and makes + it more consistent. -- smb/telnet: do not free the protocol struct in *_done() + Ref: #10818 - It is managed by the generic layer. +- runtests: more refactoring for clarity - Reported-by: Trail of Bits + Ref: #10818 - Closes #10112 +- runtests: don't start servers if -l is given -- http: use the IDN decoded name in HSTS checks +- runtests: fix typos - Otherwise it stores the info HSTS into the persistent cache for the IDN - name which will not match when the HSTS status is later checked for - using the decoded name. +- runtests: refactor singletest() into separate functions - Reported-by: Hiroki Kurosawa + This takes it from a 1200 line behemoth into something more manageable. + The content and order of the functions is taken almost directly from + singletest() so the diff sans whitespace is quite short. - Closes #10111 + Ref: #10818 -- CURLOPT_DEBUGFUNCTION.3: emphasize that incoming data is "raw" +- runtests: refactor singletest() into distinct sections - Closes #10106 + Namely: + - Verify that this test case should be run + - Start the servers needed to run this test case + - Check that test environment is fine to run this test case + - Prepare the test environment to run this test case + - Run the test command + - Clean up after test command + - Verify test succeeded -Xì Gà (16 Dec 2022) + Ref: #10818 -- socks: fix username max size is 255 (0xFF) +- runtests: stop copying a few arrays where not needed - Closes #10105 + Unlike some other languages that just copy a pointer, perl copies the + entire array contents which takes time for a large array. - Reviewed-by: Daniel Gustafsson + Ref: #10818 -Daniel Stenberg (16 Dec 2022) +- runtests: reduce redundant calls to getpart/getpartattr -- limit-rate.d: see also --rate + These functions scan through the entire test file every time to find the + right section, so they can be slow for large test files. -- lib1560: add some basic IDN host name tests + Ref: #10818 - Closes #10094 +- tests: document that the unittest keyword is special -- idn: rename the files to idn.[ch] and hold all IDN functions + Also, add other features that were missing. - Closes #10094 +Stefan Eissing (30 Mar 2023) -- idn: remove Curl_win32_ascii_to_idn +- docs: add documentation for bufq - It was not used. Introduce a new IDN header for the prototype(s). + Closes #10869 - Closes #10094 +Daniel Stenberg (30 Mar 2023) - RELEASE-NOTES: synced -- curl_url_get.3: remove spurious backtick - - Put there by mistake. - - Follow-up from 9a8564a92 - - Closes #10101 +Matt Jolly (30 Mar 2023) -- socks: fix infof() flag for outputing a char +- hostip: refuse to resolve the .onion TLD - It used to be a 'long', %lu is no longer correct. + RFC 7686 states that: - Follow-up to 57d2d9b6bed33d - Detected by Coverity CID 1517663 + > Applications that do not implement the Tor + > protocol SHOULD generate an error upon the use of .onion and + > SHOULD NOT perform a DNS lookup. - Closes #10100 + Let's do that. -- ssl-reqd.d: clarify that this is for upgrading connections only + https://www.rfc-editor.org/rfc/rfc7686#section-2 - Closes #10093 + Add test 1471 and 1472 to verify -- curl_url_set.3: document CURLU_DISALLOW_USER + Fixes #543 + Closes #10705 - Closes #10099 +Philip Heiduck (30 Mar 2023) -- cmake: set the soname on the shared library +- GHA: update ngtcp2-*.yml to v0.10.0 - Set SONAME and VERSION for platforms we think this works on. Remove - issue from KNOWN_BUGS. + Closes #10612 - Assisted-by: Jakub Zakrzewski +Stefan Eissing (30 Mar 2023) - Closes #10023 +- tests/http: fix log formatting on wrong exit code -- tool_paramhlp: free the proto strings on exit + Closes #10868 - And also make sure that repeated use of the options free the previous - string before it stores a new. +Daniel Stenberg (30 Mar 2023) - Follow-up from e6f8445edef8e7996d +- spellcheck.words: unify the AWS-LC spelling - Closes #10098 + Follow-up to 34ef4fab22d93 -- tool_cfgable: free the ssl_ec_curves on exit + Closes #10867 - Follow-up to ede125b7b +Jim King (30 Mar 2023) - Closes #10097 +- openssl: interop with AWS-LC -- urlapi: reject more bad letters from the host name: &+() + * Configure changes to detect AWS-LC + * CMakeLists.txt changes to detect AWS-LC + * Compile-time branches needed to support AWS-LC + * Correctly set OSSL_VERSION and report AWS-LC release number + * GitHub Actions script to build with autoconf and cmake against AWS-LC - Follow-up from eb0167ff7d31d3a5 + AWS-LC is a BoringSSL/OpenSSL derivative + For more information see https://github.com/awslabs/aws-lc/ - Extend test 1560 to verify + Closes #10320 - Closes #10096 +Viktor Szakats (30 Mar 2023) -- altsvc: fix rejection of negative port numbers +- cmake: picky-linker fixes for openssl, ZLIB, H3 and more - Follow-up to ac612dfeee95 + - fix HTTP/3 support detection with OpenSSL/quictls built with ZLIB. + (Requires curl be built with ZLIB option also.) - strtoul() accepts a leading minus so better make sure there is none + - fix HTTP/3 support detection with OpenSSL/quictls/LibreSSL and `ld` + linker on Windows. - Extended test 356 somewhat to use a huge negative 64 bit number that - otherwise becomes a low positive number. + - fix HTTP/3 support detection with wolfSSL to automatically add + `ws2_32` to the lib list on Windows. For all linkers. - Closes #10095 + - reposition ZLIB (and other compression) detection _after_ TLS + detection, but before calling HTTP/3-support detection via + `CheckQuicSupportInOpenSSL`. -- lib: use size_t or int etc instead of longs + May be a regression from ebef55a61df0094b9790710a42f63c48e7de3c13 + May fix #10832 (Reported-by: Micah Snyder) - Since long is not using a consistent data size in curl builds, making it - often "waste" 32 bits. + This also seems to fix an odd case, where OpenSSL/quictls is correctly + detected, but its header path is not set while compiling, breaking + build at `src/curl_ntlm_core.c`. Reason for this remains undiscovered. - Closes #10088 + - satisfy "picky" linkers such as `ld` with MinGW, that are highly + sensitive to lib order, by also adding brotli to the beginning of the + lib list. -- azure: use "unversioned" clang and clang-tools for scanbuild job + - satisfy "picky" linkers by adding certain Windows systems libs to + the lib list for OpenSSL/LibreSSL. (Might need additional ones for + other forks, such as `pthread` for BoringSSL.) - To make it less fragile + Note: It'd make sense to _always_ add `ws2_32`, `crypt32` (except + Windows App targets perhaps?), `bcrypt` (except old-mingw!) on Windows + at this point. They are almost always required, and if some aren't, + they are ignored by the linker with no effect on final binaries. - Closes #10092 + Closes #10857 -Daniel Gustafsson (14 Dec 2022) +Stefan Eissing (30 Mar 2023) -- x509asn1: avoid freeing unallocated pointers +- vlts: use full buffer size when receiving data if possible - When utf8asn1str fails there is no allocation returned, so freeing - the return pointer in **to is at best a no-op and at worst a double- - free bug waiting to happen. The current coding isn't hiding any such - bugs but to future proof, avoid freeing the return value pointer iff - the function failed. + SSL backends like OpenSSL/wolfSSL and other return the content of one + TLS record on read, but usually there are more available. - Closes: #10087 - Reviewed-by: Daniel Stenberg + Change the vtls cfilter recv() function to fill the given buffer until a + read would block. -Emil Engler (13 Dec 2022) + Closes #10736 -- curl_url_set.3: fix typo +dengjfzh on github (30 Mar 2023) - Closes: #10089 - Reviewed-by: Daniel Gustafsson +- rtsp: skip malformed RTSP interleaved frame data -Daniel Stenberg (13 Dec 2022) + Some IP cameras send malformed RTSP interleaved frames sometimes, which + can cause curl_easy_perform return 1 (CURLE_UNSUPPORTED_PROTOCOL). This + change attempts to skip clearly incorrect RTSP interleaving frame data. -- test2304: verify websocket handling when connection is closed + Closes #10808 -- server/sws: if asked to close connection, skip the websocket handling +Stefan Eissing (30 Mar 2023) -- ws: if no connection is around, return error +- lib: add `bufq` and `dynhds` - - curl_ws_send returns CURLE_SEND_ERROR if data->conn is gone + Adding `bufq`: + - at init() time configured to hold up to `n` chunks of `m` bytes each. + - various methods for reading from and writing to it. + - `peek` support to get access to buffered data without copy + - `pass` support to allow buffer flushing on write if it becomes full + - use case: IO buffers for dynamic reads and writes that do not blow up + - distinct from `dynbuf` in that: + - it maintains a read position + - writes on a full bufq return CURLE_AGAIN instead of nuking itself + - Init options: + - SOFT_LIMIT: allow writes into a full bufq + - NO_SPARES: free empty chunks right away + - a `bufc_pool` that can keep a number of spare chunks to + be shared between different `bufq` instances - - curl_ws_recv returns CURLE_GOT_NOTHING on connection close + Adding `dynhds`: + - a straightforward list of name+value pairs as used for HTTP headers + - headers can be appended dynamically + - headers can be removed again + - headers can be replaced + - headers can be looked up + - http/1.1 formatting into a `dynbuf` + - configured at init() with limits on header counts and total string + sizes + - use case: pass a HTTP request or response around without being version + specific + - express a HTTP request without a curl easy handle (used in h2 proxy + tunnels) + - future extension possibilities: + - conversions of `dynhds` to nghttp2/nghttp3 name+value arrays - - curl_ws_recv.3: mention new return code for connection close + example - embryo + Closes #10720 - Closes #10084 +- pytest: improvements for suitable curl and error output -Emil Engler (13 Dec 2022) + - will check built curl for http and https support and + skip all tests if not there + - will dump stdout/stderr/trace output on errored responses -- docs: extend the dump-header documentation + Closes #10829 - This commit extends the documentation of the --dump-header command-line - option to reflect the behavior introduced in 8b1e5df7. +Daniel Stenberg (29 Mar 2023) - See #10079 - Closes #10085 +- lib: use correct printf flags for sockets and timediffs -Daniel Stenberg (12 Dec 2022) + Introduces CURL_FORMAT_SOCKET_T for outputting socket numbers. -- RELEASE-NOTES: synced + Fixes #10737 + Reported-by: Gisle Vanem + Closes #10855 -- styled-output.d: this option does not work on Windows +- telnet: make MSVC ignore warning for assignment within conditional - Reported-by: u20221022 on github + Follow-up to d92a5007b60e0af7d - Fixes #10082 - Closes #10083 + Closes #10859 -Emil Engler (12 Dec 2022) +- ws: handle reads before EAGAIN better -- tool: determine the correct fopen option for -D + Reported-by: simplerobot on github + Fixes #10831 + Closes #10856 - This commit fixes a bug in the dump-header feature regarding the - determination of the second fopen(3) option. +- test1592: add flaky keyword - Reported-by: u20221022 on github + Closes #10860 - See #4753 - See #4762 - Fixes #10074 - Closes #10079 +Frank Gevaerts (28 Mar 2023) -Christian Schmitz (11 Dec 2022) +- lib/sha256.c: typo fix in comment (duplicated "is available") -- docs/curl_ws_send: Fixed typo in websocket docs + Closes #10851 - Replace as with is in relevant sentences. +Arne Soete (28 Mar 2023) - Closes: #10081 - Reviewed-by: Daniel Gustafsson +- tests: update tests/httpd references to tests/http -Prithvi MK (11 Dec 2022) + tests/httpd was renamed to tests/http in #10654. This patch updates some + references in the README -- c-hyper: fix multi-request mechanism + Closes #10854 - It makes test 565 run fine. +Kamil Dudka (28 Mar 2023) - Fixes #8896 - Closes #10080 - Assisted-by: Daniel Stenberg +- telnet: simplify the implementation of str_is_nonascii() -Andy Alt (11 Dec 2022) + There is no need to traverse the string twice. -- page-header: grammar improvement (display transfer rate) + Closes #10852 - Closes #10068 +Frank Gevaerts (28 Mar 2023) -- docs/DEPRECATE.md: grammar improvement and sp correction +- curl_easy_getinfo.3: typo fix (duplicated "from the") - The main thing I wanted to do was fix the spelling of "spent", but I - think this rewording improves the flow of the paragraph. + Closes #10850 - Closes #10067 +Philip Heiduck (28 Mar 2023) -Boris Verkhovskiy (11 Dec 2022) +- wolfssl.yml: bump to version 5.6.0 -- tool_cfgable: make socks5_gssapi_nec a boolean + Closes #10843 - Closes #10078 +Daniel Stenberg (28 Mar 2023) -Frank Gevaerts (9 Dec 2022) +- RELEASE-NOTES: synced -- contributors.sh: actually use $CURLWWW instead of just setting it. +Ronan Pigott (28 Mar 2023) - The script was all set up for flexibility where curl-www is elsewhere in - the filesystem, but then hard-coded ../curl-www anyway... +- docs/cmdline-opts: document the dotless config path - Closes #10064 + The real xdg config path is $XDG_CONFIG_HOME/curlrc, without the dot. + The dotless name seems preferable, so let's match the documentation to + the behavior. -Daniel Stenberg (9 Dec 2022) + Closes #10849 -- KNOWN_BUGS: remove items not considered bugs any more +Daniel Stenberg (28 Mar 2023) - - CURL_GLOBAL_SSL +- HTTP-COOKIES.md: mention the #HttpOnly_ prefix - This option was changed in libcurl 7.57.0 and clearly it has not caused - too many issues and a lot of time has passed. + Fixes #10847 + Reported-by: Harry Sintonen + Closes #10848 - - Store TLS context per transfer instead of per connection +- dynbuf: never allocate larger than "toobig" - This is a possible future optimization. One that is much less important - and interesting since the added support for CA caching. + As dynbufs always have a fixed maximum size which they are not allowed + to grow larger than, making sure that it never allocates a larger buffer + makes sure the buffer does not allocate memory that will never be used. - - Microsoft telnet server + Closes #10845 - This bug was filed in May 2007 against curl 7.16.1 and we have not - received further reports. +- ftplistparser: replace realloc with dynbuf - - active FTP over a SOCKS + Closes #10844 - Actually, proxies in general is not working with active FTP mode. This - is now added in proxy documentation. +- ftplistparser: use ISDIGIT() - - DICT responses show the underlying protocol + Closes #10844 - curl still does this, but since this is now an established behavior - since forever we cannot change it easily and adding an option for it - seems crazy as this protocol is not so little its not worth it. Let's - just live with it. +- ftplistparser: move out private data from public struct - - Secure Transport disabling hostname validation also disables SNI + The public 'curl_fileinfo' struct contained three fields that are for + internal purposes only. This change makes them unused in the public + struct. - This is an already documented restriction in Secure Transport. + The new private struct fields are also renamed to make this separation + more obvious internally. - - CURLOPT_SEEKFUNCTION not called with CURLFORM_STREAM + Closes #10844 - The curl_formadd() function is marked and documented as deprecated. No - point in collecting bugs for it. It should not be used further. +- openssl: fix indents - white space edits only - - STARTTRANSFER time is wrong for HTTP POSTs + Closes #10840 - After close source code inspection I cannot see how this is true or that - there is any special treatment for different HTTP methods. We also have - not received many further reports on this, making me strongly suspect - that this is no (longer an) issue. +- url: remove call to Curl_llist_destroy in Curl_close - - multipart formposts file name encoding + A list that is created with a NULL "destructor" does not need to be + destroyed. Not calling it is faster than calling it. - The once proposed RFC 5987-encoding is since RFC 7578 documented as MUST - NOT be used. The since then implemented MIME API allows the user to set - the name on their own and can thus provide it encoded as it wants. + Closes #10846 - - DoH is not used for all name resolves when enabled +- multi: remove PENDING + MSGSENT handles from the main linked list - It is questionable if users actually want to use DoH for interface and - FTP port name resolving. This restriction is now documented and we - advice users against using name resolving at all for these functions. + As they are not driving transfers or any socket activity, the main loop + does not need to iterate over these handles. A performance improvement. - Closes #10043 + They are instead only held in their own separate lists. -- CURLOPT_COOKIEFILE.3: advice => advise + 'data->multi' is kept a pointer to the multi handle as long as the easy + handle is actually part of it even when the handle is moved to the + pending/msgsent lists. It needs to know which multi handle it belongs + to, if for example curl_easy_cleanup() is called before the handle is + removed from the multi handle. - Closes #10063 + Alll 'data->multi' pointers of handles still part of the multi handle + gets cleared by curl_multi_cleanup() which "orphans" all previously + attached easy handles. - Reviewed-by: Daniel Gustafsson + This is take 2. The first version was reverted for the 8.0.1 release. -Daniel Gustafsson (9 Dec 2022) + Assisted-by: Stefan Eissing + Closes #10801 -- curl.h: reword comment to not use deprecated option +Stefan Eissing (26 Mar 2023) - CURLOPT_INFILE was replaced by CURLOPT_READDATA in 7.9.7, reword the - comment mentioning it to make code grepping easier as well as improve - the documentation. +- tests/http: add timeout to running curl in test cases - Closes: #10062 - Reviewed-by: Daniel Stenberg + - we had a CI case once where `curl` seemingly did not + return and it was hard to guess what happened. + - make curl execution in test cases time out after 60 seconds -Ryan Schmidt (9 Dec 2022) + Closes #10783 -- system.h: fix socklen_t, curl_off_t, long long for Classic Mac OS +Daniel Stenberg (26 Mar 2023) - Change "__MWERKS__" to "macintosh". When this block was originally added - in 3ac6929 it was probably intended to handle classic Mac OS since the - previous classic Mac OS build procedure for curl (which was removed in - bf327a9) used Metrowerks CodeWarrior. +- RELEASE-PROCEDURE: update to new schedule - But there are other classic Mac OS compilers, such as the MPW compilers, - that were not handled by this case. For classic Mac OS, - CURL_TYPEOF_CURL_SOCKLEN_T needs to match what's provided by the - third-party GUSI library, which does not vary by compiler. + Ref: https://curl.se/mail/lib-2023-03/0062.html - Meanwhile CodeWarrior works on platforms other than classic Mac OS, and - they may need different definitions. Separate blocks could be added - later for any of those platforms that curl doesn't already support. + Assisted-by: Andy Alt + Assisted-by: Dan Frandrich - Closes #10049 + Closes #10827 -- vms: remove SIZEOF_SHORT +Patrick Monnerat (26 Mar 2023) - The rest of SIZEOF_SHORT was removed in d48dd15. +- doc: curl_mime_init() strong easy handle binding has been relaxed in 7.87.0 - See #9291 - Closes #10061 + Reported-by: Chloe Kudryavtsev + Fixes #10834 + Closes #10835 -Daniel Gustafsson (8 Dec 2022) +Jay Satiro (25 Mar 2023) -- tool_formparse: avoid clobbering on function params +- CURLOPT_WRITEFUNCTION.3: fix typo - While perfectly legal to do, clobbering function parameters and using - them as local variables is confusing at best and rarely improves code - readability. Fix by using a local variable instead, no functionality - is changed. + Reported-by: Osaila@users.noreply.github.com - This also renames the parameter from data to mime_data since the term - data is (soft) reserved for the easy handle struct. + Fixes https://github.com/curl/curl/issues/10839 - Closes: #10046 - Reviewed-by: Daniel Stenberg +Dan Fandrich (24 Mar 2023) -- noproxy: guard against empty hostnames in noproxy check +- CI: skip some more builds when possible - When checking for a noproxy setting we need to ensure that we get - a hostname passed in. If there is no hostname then there cannot be - a matching noproxy rule for it by definition. + When a commit only contains tests, documentation, or cmake files, skip + those builds that aren't affected by those. - Closes: #10057 - Reported-by: Geeknik Labs - Reviewed-by: Daniel Stenberg + The file filters available on the CI services don't seem to allow + skipping individual jobs, only the entire workflow, so we can't get any + more fine-grained than this. -Daniel Stenberg (8 Dec 2022) +- CI: add and adjust labeler match patterns -- c-hyper: CONNECT respones are not server responses + Allow cmdline tool alongside other labels. - Together with d31915a8dbbd it makes test 265 run fine. +Kai Pastor (25 Mar 2023) - Fixes #8853 - Assisted-by: Prithvi MK - Assisted-by: Sean McArthur - Closes #10060 +- CMake: make config version 8 compatible with 7 -- test265: Use "connection: keep-alive" response header + Reviewed-by: Jakub Zakrzewski + Closes #10819 - When it answers as HTTP/1.0, so that clients (hyper) knows properly that - the connection remains intact. +Daniel Stenberg (24 Mar 2023) - RELEASE-NOTES: synced -Stefan Eissing (8 Dec 2022) - -- cfilter: improve SSL connection checks + Bumped version-in-progress to 8.1.0 - - fixes `Curl_ssl_cf_get_ssl()` to detect also the first filter instance - as ssl (refs #10053) +- GHA: add a memory-sanitizer job - - replaces `Curl_ssl_use()` with the correct `Curl_conn_is_ssl()` + Closes #10815 - Closes #10054 - Fixes #10053 +Dan Fandrich (23 Mar 2023) - Reported-by: Patrick Monnerat +- CI: fix brew retries on GHA -Daniel Stenberg (8 Dec 2022) + The fix in the previous commit was complete for Cirrus but accidentally + left off a part for GHA. -- runtests: silence nghttpx errors + Follow-up to c2b7249d - Also, move the output of the nghttpx_h3 info to the general "Env:" line - in the test output header. +- CI: skip Azure for more commits which change only GHA - Reported-by: Marcel Raad - Ref: https://github.com/curl/curl/commit/ca15b7512e8d1199e55fbaa206ef01e64b8f - 147d#commitcomment-92015094 - Closes #10044 +Daniel Stenberg (23 Mar 2023) -Ryan Schmidt (7 Dec 2022) +- cmake: set SONAME for SunOS too -- config-mac: define HAVE_SYS_IOCTL_H + Provided-by: Brian Lund - This is needed to compile nonblock.c on classic Mac OS with Grand - Unified Socket Interface (GUSI) because nonblock.c uses FIONBIO which is - defined in which is included by . + Closes #10816 - Ref: https://sourceforge.net/projects/gusi/ +Stefan Eissing (23 Mar 2023) - Closes https://github.com/curl/curl/pull/10042 +- ngtcp2: adjust config and code checks for ngtcp2 without nghttp3 -Philip Heiduck (7 Dec 2022) + - make configure show on HTTP3 feature that both ngtcp2 and nghttp3 + are in play + - define ENABLE_QUIC only when USE_NGTCP2 and USE_NGHTTP3 are defined + - add USE_NGHTTP3 in the ngtcp2 implementation -- CI: Change FreeBSD image from 12.3 to 12.4 + Fixes #10793 + Closes #10821 - Ref: https://www.phoronix.com/news/FreeBSD-12.4-Released +Daniel Stenberg (23 Mar 2023) - Closes https://github.com/curl/curl/pull/10051 +- data.d: emphasize no conversion -Ryan Schmidt (7 Dec 2022) + When asking curl to send a POST, curl does not encode or change the data. -- test1421: fix typo + Ref: #10820 + Closes #10823 - Closes https://github.com/curl/curl/pull/10055 +- server/getpart: clear the buffer before load -Jay Satiro (7 Dec 2022) + Fixes msan warnings: -- build: assume errno.h is always available + ==54195==WARNING: MemorySanitizer: use-of-uninitialized-value + #0 0x55ece35e57cb in line_length /home/runner/work/curl/curl/tests/server + /getpart.c:111:25 + #1 0x55ece35e3b83 in readline /home/runner/work/curl/curl/tests/server/ge + tpart.c:164:24 + #2 0x55ece35e0269 in getpart /home/runner/work/curl/curl/tests/server/get + part.c:347:18 + #3 0x55ece36180b6 in parse_servercmd /home/runner/work/curl/curl/tests/se + rver/sws.c:283:13 - - Remove errno.h detection from all build configurations. + Closes #10822 - errno.h is a standard header according to C89. +- ntlm: clear lm and nt response buffers before use - Closes https://github.com/curl/curl/pull/9986 + To avoid the risk of MemorySanitizer: use-of-uninitialized-value -- build: assume assert.h is always available + Closes #10814 - - Remove assert.h detection from all build configurations. +- digest: clear target buffer - assert.h is a standard header according to C89. + Closes #10814 - I had proposed this several years ago as part of a larger change that - was abandoned. +Douglas R. Reno (22 Mar 2023) - Ref: https://github.com/curl/curl/issues/1237#issuecomment-277500720 +- cmake: bring in the network library on Haiku. - Closes https://github.com/curl/curl/pull/9985 + When cross-compiling for Haiku, the networking library needs to be + brought in. Without this, an unknown type of "Error" is reported in + lib/curl_setup_once.h. -Philip Heiduck (7 Dec 2022) + This is also needed when using CMake natively on Haiku to build libcurl. -- CI: LGTM.com will be shut down in December 2022 + Fixes #10296 + Closes #10792 - Closes #10052 +Daniel Stenberg (22 Mar 2023) -Daniel Stenberg (6 Dec 2022) +- runtests: die if curl version can be found -- mailmap: Andy Alt + Closes #10813 -Andy Alt (6 Dec 2022) +Stefan Eissing (22 Mar 2023) -- misc: Fix incorrect spelling +- multi: add handle asserts in DEBUG builds - Fix various uses of connnect by replacing them with connect. + For GOOD_EASY_HANDLE and GOOD_MULTI_HANDLE checks - Closes: #10045 - Reviewed-by: Daniel Stenberg - Reviewed-by: Daniel Gustafsson + - allow NULL pointers to "just" return an error as before + - fail hard on nun-NULL pointers that no longer show the MAGICs -Stefan Eissing (6 Dec 2022) + Closes #10812 -- wolfssl: remove special BIO return code handling +Jon Rumsey (22 Mar 2023) - - rely solely on the retry flag in BIO, similar to OpenSSL vtls - implementation. +- gskit: various compile errors in OS400 - Ref: https://github.com/curl/curl/pull/10021#issuecomment-1336147053 + Various compile failures in gskit.c; - Closes #10033 + - pipe_ssloverssl() needs Curl_easy data parameter for + Curl_conn_cf_get_socket(cf, data) + - key_passwd is in ssl_config, not conn_config + - close_on() has 2 parameters, not 4 + - getsockopt() needs to call Curl_conn_cf_get_socket(), not + cxn->sock[FIRSTSOCKET] -Daniel Stenberg (6 Dec 2022) + Fixes #10799 + Closes #10800 -- openssl: return -1 on error in the BIO callbacks +Daniel Stenberg (22 Mar 2023) - BIO_read and BIO_write return negative numbers on error, including - retryable ones. A regression from 55807e6. Both branches should be - returning -1. +- tool_operate: pass a long as CURLOPT_HEADEROPT argument - The APIs are patterned after POSIX read and write which, similarly, - return -1 on errors, not zero, with EAGAIN treated as an error. + Closes #10798 - Bug: https://github.com/curl/curl/issues/10013#issuecomment-1335308146 - Reported-by: David Benjamin - Closes #10021 +- GHA: run all linux test jobs with valgrind -Ryan Schmidt (6 Dec 2022) + Closes #10798 -- config-mac: remove HAVE_SYS_SELECT_H +- GHA-linux: add an address-sanitizer build - When compiling for classic Mac OS with GUSI, there is no sys/select.h. - GUSI provides the "select" function prototype in sys/time.h. + Closes #10810 - Closes #10039 +Version 8.0.1 (20 Mar 2023) -- setup: do not require __MRC__ defined for Mac OS 9 builds +Daniel Stenberg (20 Mar 2023) - Partially reverts "somewhat protect Mac OS X users from using Mac OS 9 - config file", commit 62519bfe059251af2914199f284c736553ff0489. +- RELEASE-NOTES: synced - Do things that are specific to classic Mac OS (i.e. include config-mac.h - in curl_setup.h and rename "main" to "curl_main" in tool_setup.h) when - only "macintosh" is defined. Remove the additional condition that - "__MRC__" should be defined since that would only be true with the MPW - MrC compiler which prevents the use of other reasonable compilers like - the MPW SC compiler and especially the Metrowerks CodeWarrior compilers. - "macintosh" is only defined by classic Mac OS compilers so this change - should not affect users of Mac OS X / OS X / macOS / any other OS. + curl 8.0.1 - Closes #10037 +- Revert "multi: remove PENDING + MSGSENT handles from the main linked list" -- curl.h: name all public function parameters + This reverts commit f6d6f3ce01e377932f1ce7c24ee34d45a36950b8. - Most public function parameters already have names; this adds those - that were missing. + The commits caused issues in the 8.0.0 release. Needs a retake. - Closes #10036 + Reported-by: Kamil Dudka + Closes #10795 -Andy Alt (6 Dec 2022) +- include/curl/curlver.h: bump to 8.0.1 -- docs/examples: spell correction ('Retrieve') +Version 8.0.0 (20 Mar 2023) - Closes #10040 +Daniel Stenberg (20 Mar 2023) -Daniel Stenberg (6 Dec 2022) +- RELEASE-NOTES: synced -- unit1302: slightly extended + The curl 8.0.0 release - To test more base64 decoding +- THANKS: from the 8.0.0 release -- base64: faster base64 decoding +- scripts/delta: fix "ambiguous argument" when used in branches - - by using a lookup table instead of strchr() - - by doing full quantums first, then padding +- SECURITY-PROCESS.md: Busy-loops are not security problems - Closes #10032 + Closes #10790 -Michael Musset (6 Dec 2022) +Stefan Eissing (17 Mar 2023) -- libssh2: return error when ssh_hostkeyfunc returns error +- tests/http: do not save files for downloads in scorecard testing - return CURLE_PEER_FAILED_VERIFICATION if verification with the callback - return a result different than CURLKHMATCH_OK + Closes #10788 - Closes #10034 +Daniel Stenberg (17 Mar 2023) -Viktor Szakats (5 Dec 2022) +- cf-socket: use port 80 when resolving name for local bind -- Makefile.mk: improve a GNU Make hack [ci skip] + It turns out c-ares returns an error when asked to resolve a host name with + ares_getaddrinfo using port number 0. - Replace the hack of using `$() ` to represent a single space. The new - method silences the `--warn-undefined-variables` debug warning and it's - also a better-known form of solving this problem. + Reported as a c-ares bug here: https://github.com/c-ares/c-ares/issues/517 - Reviewed-by: Jay Satiro - Closes #10031 + The work-around is to simply use port 80 instead, as the number typically doe + s + not make a difference and a non-zero number works for c-ares. -Daniel Stenberg (5 Dec 2022) + Fixes #10759 + Reported-by: Matt Jolly + Closes #10789 -- tests/unit/.gitignore: ignore all unit + 4 digits files +- curl.h: require gcc 12.1 for the deprecation magic -- base64: encode without using snprintf + Reported-by: kchow-FTNT on github + Fixes #10726 + Closes #10784 - For speed. In some tests, this approch is 29 times faster! +- Revert "rtsp: use dynbuf instead of custom reallocs" - Closes #10026 + This reverts commit 1b9ea3239d22147e00d8 because of OSS-fuzz reports. + I'll do another take after the pending release. -- base64: better alloc size + Closes #10785 - The previous algorithm allocated more bytes than necessary. +- test422: verify --next used without a prior URL - Suggested-by: xtonik on github - Fixes #10024 - Closes #10025 + Closes #10782 -Ryan Schmidt (5 Dec 2022) +- tool_getparam: error if --next is used without a prior URL -- config-mac: fix typo: size_T -> size_t + Reported-by: 積丹尼 Dan Jacobson + Ref: https://github.com/curl/curl/pull/10769#discussion_r1137895629 - Both MPW and CodeWarrior compilers complained about this. + Closes #10782 - Closes #10029 +- libssh: use dynbuf instead of realloc -Daniel Stenberg (3 Dec 2022) + When building lines to show for SFTP directory listings. -- RELEASE-NOTES: synced + Closes #10778 -Jakub Zakrzewski (2 Dec 2022) +- lib2305: deal with CURLE_AGAIN -- CMake: fix build with `CURL_USE_GSSAPI` + The test does a slightly ugly busy-loop for this case but should be + managable due to it likely being a very short moment. - CMAKE_*_LINKER_FLAGS must be a string but GSS_LINKER_FLAGS is a list, so - we need to replace semicolons with spaces when setting those. + Mention CURLE_AGAIN in curl_ws_recv.3 - Fixes #9017 - Closes #1022 + Fixes #10760 + Reported-by: Jay Satiro + Closes #10781 -Max Dymond (2 Dec 2022) +- rtsp: use dynbuf instead of custom reallocs -- ci: Reuse fuzzing snippet from curl-fuzzer project + For the RTP buffering. -Diogo Teles Sant'Anna (2 Dec 2022) + Closes #10776 -- GHA: clarify workflows permissions, set least possible privilege +- libssh2: remove unused variable from libssh2's struct - Set top-level permissions to None on all workflows, setting per-job - permissions. This avoids that new jobs inherit unwanted permissions. + Closes #10777 - Discussion: https://curl.se/mail/lib-2022-11/0028.html +- RELEASE-NOTES: synced - Signed-off-by: Diogo Teles Sant'Anna +- multi: remove PENDING + MSGSENT handles from the main linked list - Closes #9928 + As they are not driving transfers or any socket activity, the main loop + does not need to iterate over these handles. A performance improvement. -Viktor Szakats (2 Dec 2022) + They are instead only held in their own separate lists. -- Makefile.mk: address minor issues + Assisted-by: Stefan Eissing + Ref: #10743 + Closes #10762 - - Fix `NROFF` auto-detection with certain shell/make-build combinations: +- multi: turn link/unlinking easy handles into dedicated functions - When a non-MSYS2 GNU Make runs inside an MSYS2 shell, Make executes - the detection command as-is via `CreateProcess()`. It fails because - `command` is an `sh` built-in. Ensure to explicitly invoke the shell. +- http_aws_sigv4: fix scan-build "value stored to 'ret' is never read" - - Initialize user-customizable variables: + Follow-up to 495d09810aa9a - Silences a list of warnings when running GNU Make with the option - `--warn-undefined-variables`. Another benefit is that it's now easy - to look up all user-customizable `Makefile.mk` variables by grepping - for ` ?=` in the curl source tree. + Closes #10766 - Suggested-by: Gisle Vanem - Ref: https://github.com/curl/curl/pull/9764#issuecomment-1330674433 +- lib: skip Curl_llist_destroy calls - - Fix `MKDIR` invocation: + Linked lists themselves do not carry any allocations, so for the lists + that do not have have a set destructor we can just skip the + Curl_llist_destroy() call and save CPU time. - Avoid a warning and potential issue in envs without forward-slash - support. + Closes #10764 - Closes #10000 +- lib643: LIB644 is never defined, this is dead code -Rob de Wit (2 Dec 2022) + Closes #10765 -- curl_get_line: allow last line without newline char +- libtest/Makefile.inc: remove superfluous variables - improve backwards compatibility + Rely on the defaults when possible. - Test 3200 verifies + Closes #10765 - Closes #9973 +- tests/http: remove year ranges from copyrights -Daniel Stenberg (2 Dec 2022) + Closes #10763 -- cookie: open cookie jar as a binary file +Casey Bodley (14 Mar 2023) - On Windows there is a difference and for text files, ^Z means end of - file which is not desirable. +- aws_sigv4: fall back to UNSIGNED-PAYLOAD for sign_as_s3 - Ref: #9973 - Closes #10017 + all s3 requests default to UNSIGNED-PAYLOAD and add the required + x-amz-content-sha256 header. this allows CURLAUTH_AWS_SIGV4 to correctly + sign s3 requests to amazon with no additional configuration -- runtests: only do CRLF replacements for hyper if it is HTTP + Signed-off-by: Casey Bodley - Closes #10016 + Closes #9995 -Stefan Eissing (1 Dec 2022) +Viktor Szakats (14 Mar 2023) -- openssl: fix for BoringSSL BIO result interpretation mixups +- wolfssl: add quic/ngtcp2 detection in cmake, and fix builds - Reported-by: Robin Marx - Fixes #10013 - Closes #10015 + - add QUIC/ngtcp2 detection in CMake with wolfSSL. -Max Dymond (1 Dec 2022) + Because wolfSSL uses zlib if available, move compression detection + before TLS detection. (OpenSSL might also need this in the future.) -- ci: Remove zuul fuzzing job as it's superseded by CIFuzz + - wolfSSL 5.5.0 started using C99 types in its `quic.h` header, but it + doesn't #include the necessary C99 header itself, breaking builds + (unless another dependency pulled it by chance.) Add local workaround + for it. For this to work with all build tools, we had to fix our + header detection first. Ref: #10745 -Daniel Stenberg (1 Dec 2022) + Ref: https://github.com/curl/curl-for-win/commit/6ad5f6ecc15620c15625fc4434 + 76b3a1ecef4f3f -- runtests: do CRLF replacements per section only + Closes #10739 - The `crlf="yes"` attribute and "hyper mode" are now only applied on a - subset of dedicated sections: data, datacheck, stdout and protocol. +Stefan Eissing (14 Mar 2023) - Updated test 2500 accordingly. +- secure-transport: fix recv return code handling - Also made test1 use crlf="yes" for , mostly because it is - often used as a template test case. Going forward, using this attribute - we should be able to write test cases using linefeeds only and avoid - mixed line ending encodings. + Return code handling of recv calls were not always correct when an error + occured or the connection was closed. - Follow-up to ca15b7512e8d11 + Closes #10717 - Fixes #10009 - Closes #10010 +- http2: Use KEEP_SEND_HOLD for flow control in HTTP/2 -Stefan Eissing (1 Dec 2022) + - use the defined, but so far not used, KEEP_SEND_HOLD bit for flow + control based suspend of sending in transfers. -- gnutls: use common gnutls init and verify code for ngtcp2 + Prior to this change KEEP_SEND_PAUSE bit was used instead, but that can + interfere with pausing streams from the user side via curl_easy_pause. - Closes #10007 + Fixes https://github.com/curl/curl/issues/10751 + Closes https://github.com/curl/curl/pull/10753 -Baitinq on github (1 Dec 2022) +Dan Fandrich (13 Mar 2023) -- aws_sigv4: fix typos in aws_sigv4.c +- tests: fix control code that hid some text in runtests.1 - Closes #10008 +- tests: sync option lists in runtests.pl & its man page -Kenneth Myhra (30 Nov 2022) +Daniel Stenberg (13 Mar 2023) -- curl.h: include on SerenityOS +- multi: make multi_perform ignore/unignore signals less often - Closes #10006 + For improved performance -Daniel Stenberg (30 Nov 2022) + Reported-by: Jerome St-Louis + Ref: #10743 + Closes #10750 -- openssl: prefix errors with '[lib]/[version]: ' +Viktor Szakats (13 Mar 2023) - To help users understand where this (cryptic) error message comes from. +- cmake: delete unused HAVE__STRTOI64 - Suggested-by: Philip Sanetra - Ref: #10002 - Closes #10004 + Also delete obsolete surrounding comments. -Stefan Eissing (30 Nov 2022) + Reviewed-by: Daniel Stenberg + Closes #10756 -- tests: add HTTP/3 test case, custom location for proper nghttpx +- CI: fix copyright header - - adding support for HTTP/3 test cases via a nghttpx server that is - build with ngtcp2 and nghttp3. - - test2500 is the first test case, performing a simple GET. - - nghttpx is checked for support and the 'feature' nghttpx-h3 - is set accordingly. test2500 will only run, when supported. - - a specific nghttpx location can be given in the environment - variable NGHTTPX or via the configure option - --with-test-nghttpx= + Follow-up to 395b9175b7422d699fa93643973295c106cdf147 - Extend NGHTTPX config to H2 tests as well +Daniel Stenberg (13 Mar 2023) - * use $ENV{NGHTTPX} and the configured default also in http2 server starts - * always provide the empty test/nghttpx.conf to nghttpx. as it defaults to - reading /etc/nghttpx/nghttpx.conf otherwise. +- RELEASE-PROCEDURE.md: update coming release dates - Added nghttpx to CI ngtcp2 jobs to run h3 tests. +Stefan Eissing (13 Mar 2023) - Closes #9031 +- tests/http: add pytest to GHA and improve tests -Daniel Stenberg (30 Nov 2022) + - added to: ngtcp2-quictls, ngtcp2-gnutls and the linux varians + quiche, bearssl, libressl, mbedtls, openssl3, rustls + - added disabled in ngtcp2-wolfssl due to weird SSL_connect() errors + not reproducable locally -- RELEASE-NOTES: synced + Improvements on pytest: - Removed duplicate after contributors.sh fix: 9967c10b6daa1 + - handling of systems with nghttpx in $PATH + - configure will seach $PATH got nghttpx used in pytest + - pytest fixes for managing nghttpx without h3 support + - ngtcp2-wolfssl: use a fully enabled wolfssl build -- scripts/contributors.sh: strip one OR MORE leading spaces + - lower parallel count for http/1.1 tests, since we do not + want to test excessive connections. + - check built curl for HTTPS-proxy support in proxy tests + - bearssl does not like one of our critical cert extensions, making + it non-critical now + - bearssl is too slow for test_12, skipping + - making sure we do h3 tests only when curl and server support is there - From names found credited in commit logs + Closes #10699 -- RELEASE-NOTES: synced +Marcel Raad (13 Mar 2023) -- openssl/mbedtls: use %d for outputing port with failf (int) +- tool_operate: silence unused parameter warning - Coverity CID 1517100 + `global` is only used in the `my_setopt` macro version without + `CURL_DISABLE_LIBCURL_OPTION` since commit 4774decf10a. - Also, remove some int typecasts in vtls.c for the port number + Closes https://github.com/curl/curl/pull/10752 - Closes #10001 +Viktor Szakats (13 Mar 2023) -- KNOWN_BUGS: remove "Multi perform hangs waiting for threaded resolver" +- build: fix stdint/inttypes detection with non-autotools - We now offer a way to avoid that hang, using CURLOPT_QUICK_EXIT. + Fix `stdint.h` and `inttypes.h` detection with non-autotools builds on + Windows. (autotools already auto-detected them accurately.) - Follow-up to 49798cac832ab1 fixed via #9147 + `lib/config-win32.h` builds (e.g. `Makefile.mk`): + - set `HAVE_STDINT_H` where supported. + - set `HAVE_INTTYPES_H` for MinGW. - Closes #9999 + CMake: + - auto-detect them on Windows. (They were both force-disabled.) + - delete unused `CURL_PULL_STDINT_H`. + - delete unused `CURL_PULL_INTTYPES_H`. + - stop detecting `HAVE_STDINT_H` twice. + Present since the initial CMake commit: 4c5307b45655ba75ab066564afdc0c111a8 + b9291 -- KNOWN_BUGS: remove "--interface for ipv6 binds to unusable IP address" + curl doesn't use these C99 headers, we need them now to workaround + broken wolfSSL builds. Ref: #10739 - Since years back the "if2ip" function verifies that it binds to a local IPv6 - address that uses the same scope as the remote address. + Once that clears up, we can delete these detections and macros (unless + we want to keep them for future us.) - This is not a bug. + Reviewed-by: Daniel Stenberg + Closes #10745 - Fixes #686 - Closes #9998 +Daniel Stenberg (13 Mar 2023) -- test1276: verify lib/optiontable.pl +- RELEASE-NOTES: synced - Checks that it generates an output identical to the file. +- ftp: add more conditions for connection reuse -- lib/optiontable.pl: adapt to CURLOPTDEPRECATED() + Reported-by: Harry Sintonen + Closes #10730 - Follow-up from 6967571bf20624bc +Dan Fandrich (12 Mar 2023) - Reported-by: Gisle Vanem +- tests: make first.c the same for both lib tests and unit tests - Fixes #9992 - Closes #9993 + The only difference used to be global variable used in unittest tests. + After cb7ed5a removed individual flag overrides for the unittests, first.c + was no longer recompiled for unit tests to include the flag, so whether it + worked or gave a link error depended on whether it was compiled in + libtest or unittest first. This way also speeds up the build by + eliminating 40 identical compile invocations. -- docs/INSTALL.md: list OSes and CPUs quoted + Fixes #10749 - to make them skip spellcheck. Also added a new CPU. +- tests: use AM_CPPFILES to modify flags in unit tests - Follow-up to 4506cbf7f24a2a + Using CPPFLAGS sometimes caused odd compile issues when building tests + with parallel make and AM_CPPFILES is the right flag, anyway. - Closes #9997 + Follow-up to cb7ed5a -Ikko Ashimine (28 Nov 2022) + Ref #10749 -- vtls: fix typo in vtls_int.h +Viktor Szakats (13 Mar 2023) - paramter -> parameter +- Makefile.mk: fix -g option in debug mode [ci skip] - Closes: #9996 - Reviewed-by: Daniel Gustafsson + Add it to `CFLAGS` (was: `LDFLAGS`). -Daniel Stenberg (28 Nov 2022) + Closes #10747 -- curl-openssl.m4: do not add $prefix/include/openssl to CPPFLAGS +Jay Satiro (12 Mar 2023) - As OpenSSL's include files are all included using in curl - source code, we just risk that existing openssl files will "shadow" - include files without path if that path is provided. +- tool: improve --stderr handling - Fixes #9989 - Closes #9988 + - freopen stderr with the user-specified file (--stderr file) instead of + using a separate 'errors' stream. -- INSTALL: update operating systems and CPU archs + - In tool_setup.h override stdio.h's stderr macro as global variable + tool_stderr. - Update after recent runs on Twitter/Mastodon and my blog + Both freopen and overriding the stderr macro are necessary because if + the user-specified filename is "-" then stdout is assigned to + tool_stderr and no freopen takes place. See the PR for more information. - Closes #9994 + Ref: https://github.com/curl/curl/issues/10491 -Stefan Eissing (28 Nov 2022) + Closes https://github.com/curl/curl/pull/10673 -- tls: backends use connection filters for IO, enabling HTTPS-proxy +Dan Fandrich (11 Mar 2023) - - OpenSSL (and compatible) - - BearSSL - - gnutls - - mbedtls - - rustls - - schannel - - secure-transport - - wolfSSL (v5.0.0 and newer) +- CI: don't run CI jobs if only another CI was changed - This leaves only the following without HTTPS-proxy support: - - gskit - - nss - - wolfSSL (versions earlier than v5.0.0) + Also skip builds on non-Windows platforms when only Windows build files + have changed. - Closes #9962 + This should reduce the number of useless builds and the associated + waiting time and chance of spurious failures, freeing resources for + new PRs. -Daniel Stenberg (28 Nov 2022) + Closes #10742 -- include/curl/curl.h: bump the deprecated requirements to gcc 6.1 +- http: don't send 100-continue for short PUT requests - Reported-by: Michael Kaufmann - Fixes #9917 - Closes #9987 + This is already how curl is documented to behave in Everything curl, but + in actuality only short POSTs skip this. This should knock 30 seconds + off a full run of the test suite since the 100-continue timeout will no + longer be hit. -Patrick Monnerat (28 Nov 2022) + Closes #10740 -- mime: relax easy/mime structures binding +- tests: add DELAY keyword to more tests using waits - Deprecation and removal of codeset conversion support from the library - have released the strict need for an early binding of mime structures to - an easy handle (https://github.com/curl/curl/commit/2610142). +- tests: hack to build most unit tests under cmake - This constraint currently forces to create the handle before the mime - structure and the latter cannot be attached to another handle once - created (see https://curl.se/mail/lib-2022-08/0027.html). + These are only built when a libcurl static library is available, since + we're not building a special libcurlu library yet and these tests rely + on private symbols that aren't available in the shared library. A few + unit tests do require libcurlu, so those are not built. - This commit removes the handle pointers from the mime structures - allowing more flexibility on their use. + Closes #10722 - When an easy handle is duplicated, bound mime structures must however - still be duplicated too as their components hold send-time dynamic - information. +- tests: fix MSVC unreachable code warnings in unit tests - Closes #9927 + Switch unit1654 to use the proper test macros as well. -fractal-access (26 Nov 2022) +- tests: make CPPFLAGS common to all unit tests -- test416: verify growing FTP file support + There's no need to specify them individually. - Added setting: RETRSIZE [size] in the section. When set this - will cause the test FTP server to return the size set (rather than the - actual size) in the acknowledgement from a RETR request. +- tests: keep cmake unit tests names in sync - Closes #9772 + Put only the test names into Makefile.inc so they can be used by both + cmake and automake. This will prevent the list of tests from becoming + out of date when they are also built under cmake. -- ftp: support growing files with CURLOPT_IGNORE_CONTENT_LENGTH +Viktor Szakats (11 Mar 2023) - When using the option CURLOPT_IGNORE_CONTENT_LENGTH (set.ignorecl in - code) to support growing files in FTP, the code should ignore the - initial size it gets from the server as this will not be the final size - of the file. This is done in ftp_state_quote() to prevent a size request - being issued in the initial sequence. However, in a later call to - ftp_state_get_resp() the code attempts to get the size of the content - again if it doesn't already have it, by parsing the response from the - RETR request. This fix prevents this parsing of the response to get the - size when the set.ignorecl option is set. This should maintain the size - value as -1, unknown, in this situation. +- src: silence wmain() warning for all build methods - Closes #9772 + llvm/clang and gcc doesn't recognize the wmain() function in Unicode + Windows builds: -Stefan Eissing (26 Nov 2022) + llvm/clang: + ``` + ../../src/tool_main.c:239:5: warning: no previous prototype for function 'wma + in' [-Wmissing-prototypes] + int wmain(int argc, wchar_t *argv[]) + ^ + 1 warning generated. + ``` -- cfilter: re-add `conn` as parameter to cfilter setup methods + gcc: + ``` + ../../src/tool_main.c:239:5: warning: no previous prototype for 'wmain' [-Wmi + ssing-prototypes] + 239 | int wmain(int argc, wchar_t *argv[]) + | ^~~~~ + ``` - - `Curl_ssl_get_config()` now returns the first config if no SSL proxy - filter is active + Before this patch, we already silenced it with CMake. This patch moves + the silencing to the source, so that it applies to all build tools. - - socket filter starts connection only on first invocation of its - connect method + Bug: https://github.com/curl/curl/issues/7229#issuecomment-1464806651 - Fixes #9982 - Closes #9983 + Reviewed-by: Marcel Raad + Closes #10744 -Daniel Stenberg (26 Nov 2022) +Dan Fandrich (10 Mar 2023) -- KNOWN_BUGS: remove five FTP related issues +- CI: fix retrying on brew failures - - "FTP with CONNECT and slow server" + The previous attempt didn't consider that the shell would exit + immediately after the false statement in the retry case. - I believe this is not a problem these days. + Follow-up to dc141a37 - - "FTP with NULs in URL parts" +Stefan Eissing (10 Mar 2023) - The FTP protocol does not support them properly anyway. +- http2: fix error handling during parallel operations - - remove "FTP and empty path parts in the URL" + RST and connection close were not handled correctly during parallel + transfers, leading to aborted response bodies being reported complete. - I don't think this has ever been reported as a real problem but was only - a hypothetical one. + Closes #10715 - - "Premature transfer end but healthy control channel" +Daniel Stenberg (10 Mar 2023) - This is not a bug, this is an optimization that *could* be performed but is - not an actual problem. +- url: only reuse connections with same GSS delegation - - "FTP without or slow 220 response" + Reported-by: Harry Sintonen + Closes #10731 - Instead add to the documentation of the connect timeout that the - connection is considered complete at TCP/TLS/QUIC layer. +Viktor Szakats (10 Mar 2023) - Closes #9979 +- lib: silence clang/gcc -Wvla warnings in brotli headers -Stefan Eissing (26 Nov 2022) + brotli v1.0.0 throughout current latest v1.0.9 and latest master [1] + trigger this warning. -- tests: add authorityInfoAccess to generated certs + It happened with CMake and GNU Make. autotools builds avoid it with + the `convert -I options to -isystem` macro. - Generate stunnel.pem as well + llvm/clang: + ``` + In file included from ./curl/lib/content_encoding.c:36: + ./brotli/x64-ucrt/usr/include/brotli/decode.h:204:34: warning: variable lengt + h array used [-Wvla] + const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ./brotli/x64-ucrt/usr/include/brotli/port.h:253:34: note: expanded from macro + 'BROTLI_ARRAY_PARAM' + ^~~~~~ + In file included from ./curl/lib/content_encoding.c:36: + ./brotli/x64-ucrt/usr/include/brotli/decode.h:206:48: warning: variable lengt + h array used [-Wvla] + uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); + ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~ + ./brotli/x64-ucrt/usr/include/brotli/port.h:253:35: note: expanded from macro + 'BROTLI_ARRAY_PARAM' + ~^~~~~ + ``` - Closes #9980 + gcc: + ``` + In file included from ./curl/lib/content_encoding.c:36: + ./brotli/x64-ucrt/usr/include/brotli/decode.h:204:5: warning: ISO C90 forbids + variable length array 'encoded_buffer' [-Wvla] + 204 | const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)], + | ^~~~~ + ./brotli/x64-ucrt/usr/include/brotli/decode.h:206:5: warning: ISO C90 forbids + variable length array 'decoded_buffer' [-Wvla] + 206 | uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]); + | ^~~~~~~ + ``` -Daniel Stenberg (25 Nov 2022) + [1] https://github.com/google/brotli/commit/ed1995b6bda19244070ab5d331111f16f + 67c8054 -- runtests: --no-debuginfod now disables DEBUGINFOD_URLS + Reviewed-by: Daniel Stenberg + Reviewed-by: Marcel Raad + Closes #10738 - Prior to this change, DEBUGINFOD_URLS was always disabled by runtests - due to a report of it slowing down tests. However, some setups need it - to fetch debug symbols, and if it is disabled on those systems then curl - tests with valgrind will fail. +Daniel Stenberg (10 Mar 2023) - Reported-by: Mark Gaiser +- curl_path: create the new path with dynbuf - Ref: #8805 - Closes #9950 + Closes #10729 -Casey Bodley (25 Nov 2022) +- url: remove dummy protocol handler -- test/aws_sigv4: test cases for content-sha256 + Just two added checks were needed saves a whole handler struct. - 1956 adds the sha256 value corresponding to an empty buffer - 1957 adds an arbitrary value and confirms that the signature differs from 195 - 6 - 1958 adds whitespace to 1957 and confirms that the signature matches 1957 - 1959 adds a value longer than 'char sha_hex[65]' in Curl_output_aws_sigv4() + Closes #10727 - Signed-off-by: Casey Bodley +Dan Fandrich (10 Mar 2023) - Closes #9804 +- CI: retry a failed brew update too, not just brew install -- aws_sigv4: consult x-%s-content-sha256 for payload hash + Also, make sure an eventual failure ends up returning a failure code so + the job stops. - `Curl_output_aws_sigv4()` doesn't always have the whole payload in - memory to generate a real payload hash. this commit allows the user to - pass in a header like `x-amz-content-sha256` to provide their desired - payload hash +Daniel Stenberg (10 Mar 2023) - some services like s3 require this header, and may support other values - like s3's `UNSIGNED-PAYLOAD` and `STREAMING-AWS4-HMAC-SHA256-PAYLOAD` - with special semantics. servers use this header's value as the payload - hash during signature validation, so it must match what the client uses - to generate the signature +- url: fix the SSH connection reuse check - CURLOPT_AWS_SIGV4.3 now describes the content-sha256 interaction + Reported-by: Harry Sintonen + Closes #10735 - Signed-off-by: Casey Bodley +- CURLOPT_PROXY.3: curl+NSS does not handle HTTPS over unix domain socket - Closes #9804 + It results in error "NSS error -5985 (PR_ADDRESS_NOT_SUPPORTED_ERROR)" -Philip Heiduck (25 Nov 2022) + Disabled test 1470 for NSS builds and documented the restriction. -- GHA: NSS use clang instead of clang-9 + Reported-by: Dan Fandrich + Fixes #10723 + Closes #10734 - Closes #9978 +- CURLSHOPT_SHARE.3: HSTS sharing is not thread-safe -Daniel Stenberg (25 Nov 2022) + Reported-by: Hiroki Kurosawa + Closes #10732 -- RELEASE-NOTES: synced +- telnet: only accept option arguments in ascii -- tool_operate: override the numeric locale and set "C" by force + To avoid embedded telnet negotiation commands etc. - Makes curl always use dot as decimal separator for options, - independently of what the locale says. Makes scripts and command lines - portable. + Reported-by: Harry Sintonen + Closes #10728 - Updated docs accordingly. +- test1903: test use of COOKIEFILE - reset - COOKIEFILE - Reported-by: Daniel Faust + This also tests for the memory leak bug fixed by parent commit b559ef6f. - Fixes #9969 - Closes #9972 + Ref: #10694 -- test1662: verify formpost, 301 redirect, no rewind possible + Closes https://github.com/curl/curl/pull/10712 - Reproduces #9735 and verifies the subsequent fix. The original issue - uses a pipe that cannot be rewound, but this test case instead sets a - callback without rewind ability to get roughly the same properties but - being a much more portable test. +Jay Satiro (10 Mar 2023) -- lib: rewind BEFORE request instead of AFTER previous +- url: fix cookielist memleak when curl_easy_reset - This makes a big difference for cases when the rewind is not actually - necessary to perofm (for example HTTP response code 301 converts to GET) - and therefore the rewind can be avoided. In particular for situations - when that rewind fails, for example when reading from a pipe or similar. + - Free set.cookelist in Curl_freeset instead of Curl_close. - Reported-by: Ali Utku Selen + Prior to this change the cookielist linked list wasn't freed by + curl_easy_reset which calls Curl_freeset to free all set. - Fixes #9735 - Closes #9958 + Bug: https://github.com/curl/curl/issues/10694#issuecomment-1458619157 + Reported-by: Sergey Ryabinin -- vtls: repair build with disabled proxy + Closes https://github.com/curl/curl/pull/10709 - Closes #9974 +Dan Fandrich (10 Mar 2023) -Daniel Gustafsson (23 Nov 2022) +- tests: fix some keywords and unused sections -- packaging: remove traces of deleted files +- tests: fix test1301 to call the right binary - Commit a8861b6cc removed packages/DOS but left a few traces of it - which broke the distcheck CI. Remove all traces. + It was refactored in commit 480ac6e5 but this step was missed. - Closes: #9971 - Reviewed-by: Daniel Stenberg +- tests: add timeout, SLOWDOWN and DELAY keywords to tests -- openssl: silence compiler warning when not using IPv6 + These are tests that are testing timing and end up being quite slow. - In non-IPv6 builds the conn parameter is unused, and compilers which - run with "-Werror=unused-parameter" (or similar) warnings turned on - fails to build. Below is an excerpt from a CI job: +Daniel Stenberg (10 Mar 2023) - vtls/openssl.c: In function ‘Curl_ossl_verifyhost’: - vtls/openssl.c:2016:75: error: unused parameter ‘conn’ [-Werror=unused- - parameter] - 2016 | CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connec - tdata *conn, - | ~~~~~~~~~~~~~ - ~~~~~~~^~~~ +- RELEASE-NOTES: synced - Closes: #9970 - Reviewed-by: Daniel Stenberg +Stefan Eissing (10 Mar 2023) -- netware: remove leftover traces +- wolfSSL: ressurect the BIO `io_result` - Commit 3b16575ae938dec2a29454631a12aa52b6ab9c67 removed support for - building on Novell Netware, but a few leftover traces remained. This - removes the last bits. + In pytest'ing the situation occored that wolfSSL reported an + IO error when the underlying BIO operation was returning an + CURLE_AGAIN condition. - Closes: #9966 - Reviewed-by: Daniel Stenberg + Readding the `io_result` filter context member to detect such + situations. -Ryan Schmidt (23 Nov 2022) + Also, making sure that the returned CURLcode is initialized + on all recv operations outcome. -- curl_endian: remove Curl_write64_le from header + Closes #10716 - The actual function was already removed in 4331c6dc. +- gssapi: align global `gss_OID_desc` vars to silence ld warnings on macOS vent + ura - See #7280 - Closes #9968 + Refs #9975 which first reported this. -Daniel Stenberg (22 Nov 2022) + Closes #10718 -- docs: add more "SEE ALSO" links to CA related pages +Daniel Stenberg (10 Mar 2023) - Closes #9959 +- libssh2: only set the memory callbacks when debugging -- examples: update descriptions + This makes us debug libssh2 less and libcurl more when for example + running torture tests that otherwise will spend a lot of time in libssh2 + functions. - Make them not say "this is an example showing..." and instead just say - what the example shows. + We leave libssh2 to test libssh2. - Closes #9960 + Closes #10721 -Stefan Eissing (22 Nov 2022) +- docs/SECURITY-PROCESS.md: updates -- vtls: localization of state data in filters + - allow Low+Medium issues to be managed through plain PRs + - update the bug-bounty part to reflect current reality - - almost all backend calls pass the Curl_cfilter intance instead of - connectdata+sockindex - - ssl_connect_data is remove from struct connectdata and made internal - to vtls - - ssl_connect_data is allocated in the added filter, kept at cf->ctx + Closes #10719 - - added function to let a ssl filter access its ssl_primary_config and - ssl_config_data this selects the propert subfields in conn and data, - for filters added as plain or proxy - - adjusted all backends to use the changed api - - adjusted all backends to access config data via the exposed - functions, no longer using conn or data directly +Dan Fandrich (9 Mar 2023) - cfilter renames for clear purpose: +- tests: fix tag markup issues in some tests - - methods `Curl_conn_*(data, conn, sockindex)` work on the complete - filter chain at `sockindex` and connection `conn`. - - methods `Curl_cf_*(cf, ...)` work on a specific Curl_cfilter - instance. - - methods `Curl_conn_cf()` work on/with filter instances at a - connection. - - rebased and resolved some naming conflicts - - hostname validation (und session lookup) on SECONDARY use the same - name as on FIRST (again). +Marcel Raad (9 Mar 2023) - new debug macros and removing connectdata from function signatures where not - needed. +- tests: add `cookies` features - adapting schannel for new Curl_read_plain paramter. + These tests don't work with `--disable-cookies`. - Closes #9919 + Closes https://github.com/curl/curl/pull/10713 -Daniel Stenberg (22 Nov 2022) +- test420: add cookies keyword -- examples/10-at-a-time: fix possible skipped final transfers + It fails with `--disable-cookies`. - Prior to this change if curl_multi_perform returned 0 running handles - and then all remaining transfers were added, then the perform loop would - end immediately without performing those transfers. + Closes https://github.com/curl/curl/pull/10713 - Reported-by: Mikhail Kuznetsov +Dan Fandrich (8 Mar 2023) - Fixes https://github.com/curl/curl/issues/9953 - Closes https://github.com/curl/curl/pull/9954 +- CI: Add more labeler match patterns -Viktor Szakats (22 Nov 2022) + Also, add the CI, tests or libcurl API tags in conjunction with any + others that might also apply. -- Makefile.mk: portable Makefile.m32 +Andy Alt (9 Mar 2023) - Update bare GNU Make `Makefile.m32` to: +- GHA: minor improvements to spellcheck - - Move objects into a subdirectory. - - Add support for MS-DOS. Tested with DJGPP. - - Add support for Watt-32 (on MS-DOS). - - Add support for AmigaOS. - - Rename `Makefile.m32` to `Makefile.mk` - - Replace `ARCH` with `TRIPLET`. - - Build `tool_hugehelp.c` proper (when tools are available). - - Drop MS-DOS compatibility macro `USE_ZLIB` (replaced by `HAVE_LIBZ`) - - Add support for `ZLIB_LIBS` to override `-lz`. - - Omit object files when building examples. - - Default `CC` to `gcc` once again, for convenience. (Caveat: compiler - name `cc` cannot be set now.) - - Set `-DCURL_NO_OLDIES` for examples, like autotools does. - - Delete `makefile.dj` files. Notice the configuration details and - defaults are not retained with the new method. - - Delete `makefile.amiga` files. A successful build needs a few custom - options. We're also not retaining all build details from the existing - Amiga make files. - - Rename `Makefile.m32` to `Makefile.mk` to reflect that they are not - Windows/MinGW32-specific anymore. - - Add support for new `CFG` options: `-map`, `-debug`, `-trackmem` - - Set `-DNDEBUG` by default. - - Allow using `-DOS=...` in all `lib/config-*.h` headers, syncing this - with `config-win32.h`. - - Look for zlib parts in `ZLIB_PATH/include` and `ZLIB_PATH/lib` - instead of bare `ZLIB_PATH`. + Closes #10640 - Note that existing build configurations for MS-DOS and AmigaOS likely - become incompatible with this change. +Daniel Stenberg (9 Mar 2023) - Example AmigaOS configuration: - ``` - export CROSSPREFIX=/opt/amiga/bin/m68k-amigaos- - export CC=gcc - export CPPFLAGS='-DHAVE_PROTO_BSDSOCKET_H' - export CFLAGS='-mcrt=clib2' - export LDFLAGS="${CFLAGS}" - export LIBS='-lnet -lm' - make -C lib -f Makefile.mk - make -C src -f Makefile.mk - ``` +- test1671: fix after fix - Example MS-DOS configuration: - ``` - export CROSSPREFIX=/opt/djgpp/bin/i586-pc-msdosdjgpp- - export WATT_PATH=/opt/djgpp/net/watt - export ZLIB_PATH=/opt/djgpp - export OPENSSL_PATH=/opt/djgpp - export OPENSSL_LIBS='-lssl -lcrypt' - export CFG=-zlib-ssl - make -C lib -f Makefile.mk - make -C src -f Makefile.mk - ``` +- test421: -w %{header_json} test with multiple same header names - Closes #9764 + To reproduce the issue in #10704 -Stefan Eissing (22 Nov 2022) +- tool_writeout_json. fix the output for duplicate header names -- cfiler: filter types have flags indicating what they do + Header entries with index != 0 are handled at the index 0 level so they + should then be skipped when iterated over. - - Adding Curl_conn_is_ip_connected() to check if network connectivity - has been reached + Reported-by: Boris Okunskiy + Fixes #10704 + Closes #10707 - - having ftp wait for network connectivity before proceeding with - transfers. +- headers: make curl_easy_header and nextheader return different buffers - Fixes test failures 1631 and 1632 with hyper. + By letting curl_easy_header() and curl_easy_nextheader() store the + header data in their own struct storage when they return a pointer to + it, it makes it possible for applications to use them both in a loop. + Like the curl tool does. - Closes #9952 + Reported-by: Boris Okunskiy + Fixes #10704 + Closes #10707 -Daniel Stenberg (21 Nov 2022) +rcombs (8 Mar 2023) -- RELEASE-NOTES: synced +- urlapi: take const args in _dup and _get functions -Jay Satiro (20 Nov 2022) + Closes #10708 -- sendf: change Curl_read_plain to wrap Curl_recv_plain (take 2) +- urlapi: avoid mutating internals in getter routine - Prior to this change Curl_read_plain would attempt to read the - socket directly. On Windows that's a problem because recv data may be - cached by libcurl and that data is only drained using Curl_recv_plain. + This was not intended. - Rather than rewrite Curl_read_plain to handle cached recv data, I - changed it to wrap Curl_recv_plain, in much the same way that - Curl_write_plain already wraps Curl_send_plain. + Closes #10708 - Curl_read_plain -> Curl_recv_plain - Curl_write_plain -> Curl_send_plain +Daniel Stenberg (8 Mar 2023) - This fixes a bug in the schannel backend where decryption of arbitrary - TLS records fails because cached recv data is never drained. We send - data (TLS records formed by Schannel) using Curl_write_plain, which - calls Curl_send_plain, and that may do a recv-before-send - ("pre-receive") to cache received data. The code calls Curl_read_plain - to read data (TLS records from the server), which prior to this change - did not call Curl_recv_plain and therefore cached recv data wasn't - retrieved, resulting in malformed TLS records and decryption failure - (SEC_E_DECRYPT_FAILURE). +- urlapi: '%' is illegal in host names - The bug has only been observed during Schannel TLS 1.3 handshakes. Refer - to the issue and PR for more information. + Update test 1560 to verify - -- + Ref: #10708 + Closes #10711 - This is take 2 of the original fix. It preserves the original behavior - of Curl_read_plain to write 0 to the bytes read parameter on error, - since apparently some callers expect that (SOCKS tests were hanging). - The original fix which landed in 12e1def5 and was later reverted in - 18383fbf failed to work properly because it did not do that. +- ftp: make the 'ftpauth' a more normal 'char *'-array - Also, it changes Curl_write_plain the same way to complement - Curl_read_plain, and it changes Curl_send_plain to return -1 instead of - 0 on CURLE_AGAIN to complement Curl_recv_plain. + Closes #10703 - Behavior on error with these changes: +Evgeny Grin (Karlson2k) (8 Mar 2023) - Curl_recv_plain returns -1 and *code receives error code. - Curl_send_plain returns -1 and *code receives error code. - Curl_read_plain returns error code and *n (bytes read) receives 0. - Curl_write_plain returns error code and *written receives 0. +- doc: fix compiler warning in libcurl.m4 - -- + Current test for curl_free() may produce warnings with strict compiler + flags or even with default compiler flags with upcoming versions. + These warning could turned into errors by -Werror or similar flags. + Such warnings/errors are avoided by this patch. - Ref: https://github.com/curl/curl/issues/9431#issuecomment-1312420361 + Closes #10710 - Assisted-by: Joel Depooter - Reported-by: Egor Pugin +Viktor Szakats (8 Mar 2023) - Fixes https://github.com/curl/curl/issues/9431 - Closes https://github.com/curl/curl/pull/9949 +- misc: fix typos -Sean McArthur (19 Nov 2022) + Closes #10706 -- hyper: classify headers as CONNECT and 1XX +Stefan Eissing (7 Mar 2023) - Closes #9947 +- ftp: active mode with SSL, add the damn filter -Stefan Eissing (19 Nov 2022) + - since 7.87.0 we lost adding the SSL filter for an active + FTP connection that uses SSL. This leads to hangers and timeouts + as reported in #10666. -- ftp: fix "AUTH TLS" on primary conn and for SSL in PASV second conn + Reported-by: SandakovMM on github + Fixes #10666 + Closes #10669 - Follow-up to dafdb20a26d0c89 +Daniel Stenberg (7 Mar 2023) - Reported-by: Anthony Hu - Closes #9948 +- docs: extend the URL API descriptions -Jay Satiro (19 Nov 2022) + Closes #10701 -- CURLOPT_POST.3: Explain setting to 0 changes request type +Stefan Eissing (7 Mar 2023) - Bug: https://github.com/curl/curl/issues/9849 - Reported-by: MonkeybreadSoftware@users.noreply.github.com +- url: fix logic in connection reuse to deny reuse on "unclean" connections - Closes https://github.com/curl/curl/pull/9942 + - add parameter to `conn_is_alive()` cfilter method that returns + if there is input data waiting on the connection + - refrain from re-using connnection from the cache that have + input pending + - adapt http/2 and http/3 alive checks to digest pending input + to check the connection state + - remove check_cxn method from openssl as that was just doing + what the socket filter now does. + - add tests for connection reuse with special server configs -Daniel Stenberg (19 Nov 2022) + Closes #10690 -- docs/INSTALL.md: expand on static builds +Daniel Stenberg (6 Mar 2023) - Remove from KNOWN_BUGS +- x509asn1: use plain %x, not %lx, when the arg is an int - Closes #9944 + Pointed out by Coverity. -Stefan Eissing (19 Nov 2022) + Closes #10689 -- http: restore h3 to working condition after connection filter introduction +Stefan Eissing (6 Mar 2023) - Follow-up to dafdb20a26d0c +- http2: fix handling of RST and GOAWAY to recognize partial transfers - HTTP/3 needs a special filter chain, since it does the TLS handling - itself. This PR adds special setup handling in the HTTP protocol handler - that takes are of it. + - a reset transfer (HTTP/2 RST) did not always lead to the proper + error message on receiving its response, leading to wrong reports + of a successful transfer + - test_05_02 was able to trigger this condition with increased transfer + count. The simulated response errors did not carry a 'Content-Length' + so only proper RST handling could detect the abort + - When doing such transfers in parallel, a connection could enter the + state where + a) it had been closed (GOAWAY received) + b) the RST had not been "seen" for the transfer yet + or c) the GOAWAY announced an error and the last successful + stream id was not checked against ongoing transfers - When a handler, in its setup method, installs filters, the default - behaviour for managing the filter chain is overridden. + Closes #10693 - Reported-by: Karthikdasari0423 on github +- tests: use dynamic ports numbers in pytest suite - Fixes #9931 - Closes #9945 + - necessary ports are bound at start of test suite and then + given to server fixtures for use. + - this make parallel use of pytest (in separate directories), + practically safe for use as OS tend to not reuse such port numbers + for a while -Daniel Stenberg (18 Nov 2022) + Closes #10692 -- urldata: change port num storage to int and unsigned short +- connect: fix time_connect and time_appconnect timer statistics - Instead of long. + - time_connect was not updated when the overall connection failed, + e.g. when SSL verification was unsuccessful, refs #10670 + - rework gather those values to interrogate involved filters, + also from all eyeballing attempts, to report the maximum of + those values. + - added 3 test cases in test_06 to check reported values on + successful, partially failed and totally failed connections. - Closes #9946 + Reported-by: Master Inspire + Fixes #10670 + Closes #10671 -- Revert "sendf: change Curl_read_plain to wrap Curl_recv_plain" +Daniel Stenberg (6 Mar 2023) - This reverts commit 12e1def51a75392df62e65490416007d7e68dab9. +- test1905: update output cookie order - It introduced SOCKS proxy fails, like test 700 never ending. + After the #10685 update - Reopens #9431 +- test420: verify expiring cookies -- HTTP-COOKIES.md: update the 6265bis link to draft-11 + Cookies that are loaded fine from a jar but then are expired in headers. - Closes #9940 +- cookie: don't load cookies again when flushing -- docs/WEBSOCKET.md: explain the URL use + Reported-by: Sergio Mijatovic + Fixes #10677 + Closes #10685 - Fixes #9936 - Closes #9941 +- RELEASE-NOTES: synced -Jay Satiro (18 Nov 2022) +Andy Alt (6 Mar 2023) -- sendf: change Curl_read_plain to wrap Curl_recv_plain +- docs: note '--data-urlencode' option - Prior to this change Curl_read_plain would attempt to read the - socket directly. On Windows that's a problem because recv data may be - cached by libcurl and that data is only drained using Curl_recv_plain. + Closes #10687 - Rather than rewrite Curl_read_plain to handle cached recv data, I - changed it to wrap Curl_recv_plain, in much the same way that - Curl_write_plain already wraps Curl_send_plain. +Daniel Stenberg (6 Mar 2023) - Curl_read_plain -> Curl_recv_plain - Curl_write_plain -> Curl_send_plain +- DEPRECATE: the original legacy mingw version 1 - This fixes a bug in the schannel backend where decryption of arbitrary - TLS records fails because cached recv data is never drained. We send - data (TLS records formed by Schannel) using Curl_write_plain, which - calls Curl_send_plain, and that may do a recv-before-send - ("pre-receive") to cache received data. The code calls Curl_read_plain - to read data (TLS records from the server), which prior to this change - did not call Curl_recv_plain and therefore cached recv data wasn't - retrieved, resulting in malformed TLS records and decryption failure - (SEC_E_DECRYPT_FAILURE). + Remove completely in September 2023 - The bug has only been observed during Schannel TLS 1.3 handshakes. Refer - to the issue and PR for more information. + Closes #10667 - Ref: https://github.com/curl/curl/issues/9431#issuecomment-1312420361 +Harry Sintonen (6 Mar 2023) - Assisted-by: Joel Depooter - Reported-by: Egor Pugin +- rand: use arc4random as fallback when available - Fixes https://github.com/curl/curl/issues/9431 - Closes https://github.com/curl/curl/pull/9904 + Normally curl uses cryptographically strong random provided by the + selected SSL backend. If compiled without SSL support, a naive built-in + function was used instead. -- test3026: reduce runtime in legacy mingw builds + Generally this was okay, but it will result in some downsides for non- + SSL builds, such as predictable temporary file names. - - Load Windows system libraries secur32 and iphlpapi beforehand, so - that libcurl's repeated global init/cleanup only increases/decreases - the library's refcount rather than causing it to load/unload. + This change ensures that arc4random will be used instead, if available. - Assisted-by: Marc Hoersken + Closes #10672 - Closes https://github.com/curl/curl/pull/9412 +Grisha Levit (6 Mar 2023) -Daniel Stenberg (18 Nov 2022) +- tool: dump headers even if file is write-only -- url: move back the IDN conversion of proxy names + The fixes in #10079 brought a (seemingly unrelated) change of open mode + from `wb`/`ab` to `wb+`/`ab+` for the headerfile. This makes it no + longer possible to write the header file to e.g. a pipe, like: - Regression: in commit 53bcf55 we moved the IDN conversion calls to - happen before the HSTS checks. But the HSTS checks are only done on the - server host name, not the proxy names. By moving the proxy name IDN - conversions, we accidentally broke the verbose output showing the proxy - name. + curl -D >(grep ...) file:///dev/null - This change moves back the IDN conversions for the proxy names to the - place in the code path they were before 53bcf55. + Which presently results in `Warning: Failed to open /dev/fd/63` - Reported-by: Andy Stamp - Fixes #9937 - Closes #9939 + See #10079 + Closes #10675 diff --git a/vendor/curl/RELEASE-NOTES b/vendor/curl/RELEASE-NOTES index c091fa8f79..0fc352c458 100644 --- a/vendor/curl/RELEASE-NOTES +++ b/vendor/curl/RELEASE-NOTES @@ -1,27 +1,199 @@ -curl and libcurl 8.1.2 +curl and libcurl 8.3.0 - Public curl releases: 219 - Command line options: 251 - curl_easy_setopt() options: 302 - Public functions in libcurl: 91 - Contributors: 2888 + Public curl releases: 251 + Command line options: 257 + curl_easy_setopt() options: 303 + Public functions in libcurl: 92 + Contributors: 2977 + +This release includes the following changes: + + o curl: make %output{} in -w specify a file to write to [36] + o gskit: remove [71] + o lib: --disable-bindlocal builds curl without local binding support + o nss: remove support for this TLS library [10] + o tool: add "variable" support [1] + o trace: make tracing available in non-debug builds [41] + o url: change default value for CURLOPT_MAXREDIRS to 30 [46] + o urlapi: CURLU_PUNY2IDN - convert from punycode to IDN name [54] + o wolfssl: support loading system CA certificates [8] This release includes the following bugfixes: - o configure: quote the assignments for run-compiler [1] - o configure: without pkg-config and no custom path, use -lnghttp2 [8] - o curl: cache the --trace-time value for a second [9] - o http2: fix EOF handling on uploads with auth negotiation [7] - o http3: send EOF indicator early as possible [11] - o lib1560: verify more scheme guessing [5] - o lib: remove unused functions, make single-use static [3] - o libcurl.m4: remove trailing 'dnl' that causes this to break autoconf [10] - o libssh: when keyboard-interactive auth fails, try password [4] - o misc: fix spelling mistakes [2] - o page-header: mention curl version and how to figure out current release [13] - o page-header: minor wording polish in the URL segment [12] - o scripts/singleuse.pl: add more API calls - o urlapi: remove superfluous host name check [6] + o altsvc: accept and parse IPv6 addresses in response headers [113] + o asyn-ares: reduce timeout to 2000ms [148] + o aws-sigv4: canonicalize the query [127] + o aws-sigv4: fix having date header twice in some cases [141] + o aws-sigv4: handle no-value user header entries [159] + o bearssl: don't load CA certs when peer verification is disabled [33] + o bearssl: handshake fix, provide proper get_select_socks() implementation [99] + o build: fix portability of mancheck and checksrc targets + o build: streamline non-UWP wincrypt detections [87] + o c-hyper: adjust the hyper to curlcode conversion [52] + o c-hyper: fix memory leaks in `Curl_http` [126] + o cf-haproxy: make CURLOPT_HAPROXY_CLIENT_IP set the *source* IP [61] + o cf-socket: log successful interface bind [39] + o CI/cirrus: disable python install on FreeBSD [83] + o CI: add a 32-bit i686 Linux build [158] + o CI: add caching to many jobs [19] + o CI: move on to ngtcp2 v0.19.1 [154] + o CI: move the Alpine build from Cirrus to GHA + o CI: ngtcp2-linux: use separate caches for tls libraries [125] + o CI: remove Windows builds from Cirrus, without replacement [131] + o CI: switch macOS ARM build from Cirrus to Circle CI + o CI: use master again for wolfssl + o cirrus: install everthing with pkg, avoid pip [110] + o cmake: add GnuTLS option [103] + o cmake: add support for `CURL_DEFAULT_SSL_BACKEND` [128] + o cmake: add support for single libcurl compilation pass [21] + o cmake: allow `SHARE_LIB_OBJECT=ON` on all platforms [80] + o cmake: assume `wldap32` availability on Windows [81] + o cmake: cache more config and delete unused ones [4] + o cmake: detect `SSL_set0_wbio` in OpenSSL [22] + o cmake: drop `HAVE_LIBWINMM` and `HAVE_LIBWS2_32` feature checks [68] + o cmake: fix to use variable for the curl namespace [79] + o cmake: fixup H2 duplicate symbols for unity builds [23] + o cmake: set SIZEOF_LONG_LONG in curl_config.h [165] + o cmake: support building static and shared libcurl in one go [17] + o cmdline-docs: make sure to phrase it as "added in ...." [161] + o cmdline-docs: use present tense, not future [160] + o cmdline-opts/docs: mention the negative option part [90] + o cmdline-opts/page-header: clarify stronger that !opt == URL [123] + o cmdline-opts/page-header: reorder, clean up [51] + o configure, cmake, lib: more form api deprecation [7] + o configure: fix `HAVE_TIME_T_UNSIGNED` check [153] + o configure: trust pkg-config when it's used for zlib [149] + o configure: use the pkg-config --libs-only-l flag for libssh2 [16] + o connect: stop halving the remaining timeout when less than 600 ms left [147] + o cookie-jar.d: emphasize that this option is ONLY writing cookies [72] + o crypto: ensure crypto initialization works [69] + o curl_url_get/set.3: add missing semicolon in SYNOPSIS + o CURLINFO_CERTINFO.3: better explain curl_certinfo struct [64] + o CURLINFO_TLS_SSL_PTR.3: clarify a recommendation [75] + o CURLOPT_*TIMEOUT*: extend and clarify [101] + o CURLOPT_SSL_VERIFYPEER.3: mention it does not load CA certs when disabled [42] + o CURLOPT_URL.3: add two URL API calls in the see-also section + o CURLOPT_URL.3: explain curl_url_set() uses the same parser + o digest: Use hostname to generate spn instead of realm [164] + o disable.d: explain --disable not implemented prior to 7.50.0 [115] + o docs/cmdline-opts/gen.pl: hide "added in" before 7.50.0 [76] + o docs/cmdline-opts: match the current output [104] + o docs/cmdline-opts: spellfixes, typos and polish [9] + o docs/cmdline: add small "warning" to verbose options [59] + o docs/cmdline: remove repeated working for negotiate + ntlm [58] + o docs/HYPER.md: document a workaround for a link error [73] + o docs: add curl_global_trace to some SEE ALSO sections [133] + o docs: link to the website versions instead of markdowns [3] + o docs: mark --ssl-revoke-best-effort as Schannel specific [162] + o docs: mention critical files in same directories as curl saves [119] + o docs: removing "pausing transfers" from HYPER.md. [134] + o docs: rewrite to present tense [105] + o easy: remove #ifdefs to make code easier on the eye [34] + o egd: delete feature detection and related source code [5] + o ftp: fix temp write of ipv6 address [143] + o gen.pl: escape all dashes (ascii minus) to avoid unicode hyphens [50] + o gen.pl: replace all single quotes with aq [78] + o GHA: adding quiche workflow [35] + o headers: accept leading whitespaces on first response header [37] + o http2: avoid too early connection re-use/multiplexing [20] + o http2: cleanup trace messages [56] + o http2: disable asssertion blocking OSSFuzz testing [31] + o http2: fix in h2 proxy tunnel: progress in ingress on sending [32] + o http2: polish things around POST [132] + o http2: upgrade tests and add fix for non-existing stream [44] + o http3/ngtcp2: shorten handshake, trace cleanup [13] + o http3: quiche, handshake optimization, trace cleanup [63] + o http: close the connection after a late 417 is received [109] + o http: do not require a user name when using CURLAUTH_NEGOTIATE [86] + o http: fix sending of large requests [156] + o http: remove the p_pragma struct field [60] + o http: return error when receiving too large header set [43] + o hyper: fix a progress upload counter bug [122] + o hyper: fix ownership problems [116] + o hyper: remove `hyptransfer->endtask` [137] + o imap: add a check for failing strdup() + o imap: remove the only sscanf() call in the IMAP code [84] + o include.d: explain headers not printed with --fail before 7.75.0 [155] + o include/curl/mprintf.h: add __attribute__ for the prototypes [38] + o krb5: fix "implicit conversion loses integer precision" warnings [152] + o lib: add ability to disable auths individually [135] + o lib: build fixups when built with most things disabled [97] + o lib: fix a few *printf() flag mistakes [47] + o lib: fix null ptr derefs and uninitialized vars (h2/h3) [107] + o lib: move mimepost data from ->req.p.http to ->state [94] + o libtest: use curl_free() to free libcurl allocated data [114] + o list-only.d: mention SFTP as supported protocol [55] + o macOS: fix target detection more [11] + o misc: fix various typos [18] + o multi.h: the 'revents' field of curl_waitfd is supported [117] + o multi: more efficient pollfd count for poll [130] + o multi: remove 'processing: ' debug message [142] + o ngtcp2: fix handling of large requests [150] + o openssl: auto-detect `SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED` [65] + o openssl: clear error queue after SSL_shutdown [120] + o openssl: make aws-lc version support OCSP [48] + o openssl: Support async cert verify callback [24] + o openssl: switch to modern init for LibreSSL 2.7.0+ [70] + o openssl: use `SSL_CTX_set_ciphersuites` with LibreSSL 3.4.1 [66] + o openssl: use `SSL_CTX_set_keylog_callback` with LibreSSL 3.5.0 [67] + o openssl: when CURLOPT_SSL_CTX_FUNCTION is registered, init x509 store before [151] + o os400: build test servers [136] + o os400: do not check translatable options at build time [95] + o os400: implement CLI tool [140] + o page-footer: QLOGDIR works with ngtcp2 and quiche [62] + o page-header: move up a URL paragraph from GLOBBING to URL + o pytest: fix check for slow_network skips to only apply when intended [157] + o quic: don't set SNI if hostname is an IP address [166] + o quiche: adjust quiche `QUIC_IDLE_TIMEOUT` to 60s + o quiche: enable quiche to handle timeout events [82] + o resolve: use PF_INET6 family lookups when CURL_IPRESOLVE_V6 is set [2] + o revert "schannel: reverse the order of certinfo insertions" [14] + o schannel: fix ordering of cert chain info [163] + o schannel: fix user-set legacy algorithms in Windows 10 & 11 [53] + o schannel: verify hostname independent of verify cert [74] + o sectransp: fix compiler warnings [129] + o sectransp: prevent CFRelease() of NULL [26] + o secureserver.pl: fix stunnel path quoting [112] + o secureserver.pl: fix stunnel version parsing [111] + o SECURITY-PROCESS.md: not a sec issue: Tricking user to run a cmdline [146] + o system.h: add CURL_OFF_T definitions on HP-UX with HP aCC [108] + o test1304: build and skip without netrc support + o test1554: check translatable string options in OS400 wrapper [96] + o test1608: make it build and get skipped without shuffle DNS support + o test687/688: two more basic --xattr tests [89] + o tests/tftpd+mqttd: make variables static to silence picky warnings [57] + o tests: add 'large-time' as a testable feature [92] + o tests: add support for nested %if conditions [91] + o tests: don't call HTTP errors OK in test cases + o tests: ensure `libcurl.def` contains all exports [45] + o tests: fix h3 server check and parallel instances [6] + o tests: TLS session sharing test [100] + o tests: update cookie expiry dates to far in the future [121] + o time-cond.d: mention what happens on a missing file [93] + o tool: avoid including leading spaces in the Location hyperlink [145] + o tool: change some fopen failures from warnings to errors [144] + o tool: make the length argument an int for printf()-.* flags [49] + o tool_cb_wrt: fix invalid unicode for windows console [25] + o tool_filetime: make -z work with file dates before 1970 [139] + o tool_operate: allow both SSL_CERT_FILE and SSL_CERT_DIR [12] + o tool_operate: make aws-sigv4 not require TLS to be used + o tool_paramhlp: improve str2num(): avoid unnecessary call to strlen() [118] + o tool_urlglob: use the correct format specifier for curl_off_t in msnprintf [88] + o transfer: also stop the sending on closed connection [124] + o transfer: don't set TIMER_STARTTRANSFER on first send [77] + o unit2600: fix build warning if built without verbose messages + o url: remove infof() output for "still name resolving" [28] + o urlapi: fix heap buffer overflow [30] + o urlapi: make sure zoneid is also duplicated in curl_url_dup [29] + o urlapi: return CURLUE_BAD_HOSTNAME if puny2idn encoding fails [102] + o urlapi: setting a blank URL ("") is not an ok URL [106] + o vquic: show stringified messages for errno [40] + o vtls: clarify "ALPN: offers" message [27] + o winbuild: improve check for static zlib [15] + o wolfSSL: avoid the OpenSSL compat API when not needed [85] + o workflows/macos.yml: disable zstd and alt-svc in the http-only build [98] + o write-out.d: clarify %{time_starttransfer} + o ws: fix spelling mistakes in examples and tests [138] This release includes the following known bugs: @@ -29,8 +201,6 @@ This release includes the following known bugs: Planned upcoming removals include: - o gskit - o NSS o support for space-separated NOPROXY patterns o support for the original legacy mingw version 1 @@ -39,23 +209,193 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Aleksander Mazur, Christian Hesse, correctmost on github, Dan Fandrich, - Daniel Stenberg, Emanuele Torre, Gisle Vanem, Kev Jackson, - musvaage on github, Sergey Fionov, Stefan Eissing, Viktor Szakats, 左潇峰 - (13 contributors) + ad0p on github, Alexander Jaeger, Alexander Kanavin, apparentorder on github, + balikalina on Github, Benoit Pierre, Chris Talbot, Christian Hesse, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dan Jacobson, + Dave Cottlehuber, Davide Masserut, Derzsi Dániel, Douglas R. Reno, + ed0d2b2ce19451f2, Emanuele Torre, Enrico Scholz, eppesuig, FC Stegerman, + Gabriel Corona, Gerome Fournier, Gisle Vanem, Goro FUJI, Graham Campbell, + Guillaume Algis, guoxinvmware on github, Harry Sintonen, Jacob Mealey, + JazJas on github, John Bampton, John Hawthorn, John Walker, Joseph Tharayil, + junsik on github, kyled-dell on github, Lukas Tribus, Maksim Arhipov, + Maksim Sciepanienka, Marcel Raad, Marin Hannache, Markus Sommer, + Martin Galvan, Mathew Benson, Matthias Gatto, Maurício Meneghini Fauth, + Michael Osipov, Mohamed Daahir, Nathan Moinvaziri, Niall McGee, + Nicholas Nethercote, Nicolas Noben, Nicolás Ojeda Bär, Oleg Jukovec, + oliverpool on github, Pablo Busse, Patrick Monnerat, + Philippe Antoine on HackerOne, pszlazak on github, Randall S. Becker, + Ray Satiro, Richard W.M. Jones, Rutger Broekhoff, Ryan Schmidt, + Samuel Chiang, Satana de Sant'Ana, Sergey, Sevan Janiyan, Stefan Eissing, + Thomas M. DuBuisson, Thorsten Klein, trrui-huawei, Viktor Szakats, vvb2060, + wangzhikun, Wilhelm von Thiele, Wyatt O'Day, yushicheng7788 on github, + zhihaoy on github + (80 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=11179 - [2] = https://curl.se/bug/?i=11171 - [3] = https://curl.se/bug/?i=11174 - [4] = https://curl.se/bug/?i=11196 - [5] = https://curl.se/bug/?i=11219 - [6] = https://curl.se/bug/?i=11195 - [7] = https://curl.se/bug/?i=11194 - [8] = https://curl.se/bug/?i=11186 - [9] = https://curl.se/bug/?i=11211 - [10] = https://curl.se/bug/?i=11212 - [11] = https://curl.se/bug/?i=11205 - [12] = https://curl.se/bug/?i=11217 - [13] = https://curl.se/bug/?i=11216 + [1] = https://curl.se/bug/?i=11346 + [2] = https://curl.se/bug/?i=11564 + [3] = https://github.com/curl/curl-www/issues/272 + [4] = https://curl.se/bug/?i=11551 + [5] = https://curl.se/bug/?i=11556 + [6] = https://curl.se/bug/?i=11553 + [7] = https://curl.se/bug/?i=9621 + [8] = https://curl.se/bug/?i=11452 + [9] = https://curl.se/bug/?i=11562 + [10] = https://curl.se/bug/?i=11459 + [11] = https://curl.se/bug/?i=11502 + [12] = https://curl.se/bug/?i=11325 + [13] = https://curl.se/bug/?i=11609 + [14] = https://curl.se/bug/?i=11536 + [15] = https://curl.se/bug/?i=11521 + [16] = https://curl.se/bug/?i=11538 + [17] = https://curl.se/bug/?i=11505 + [18] = https://curl.se/bug/?i=11561 + [19] = https://curl.se/bug/?i=11532 + [20] = https://curl.se/mail/lib-2023-07/0045.html + [21] = https://curl.se/bug/?i=11546 + [22] = https://curl.se/bug/?i=11555 + [23] = https://curl.se/bug/?i=11550 + [24] = https://curl.se/bug/?i=11499 + [25] = https://curl.se/bug/?i=9841 + [26] = https://curl.se/bug/?i=9194 + [27] = https://curl.se/mail/lib-2023-07/0041.html + [28] = https://curl.se/bug/?i=11394 + [29] = https://curl.se/mail/lib-2023-07/0047.html + [30] = https://curl.se/bug/?i=11560 + [31] = https://curl.se/bug/?i=11500 + [32] = https://curl.se/bug/?i=11527 + [33] = https://curl.se/bug/?i=11457 + [34] = https://curl.se/bug/?i=11525 + [35] = https://curl.se/bug/?i=11517 + [36] = https://curl.se/bug/?i=11416 + [37] = https://curl.se/bug/?i=11605 + [38] = https://curl.se/bug/?i=11589 + [39] = https://curl.se/bug/?i=11608 + [40] = https://curl.se/bug/?i=11584 + [41] = https://curl.se/bug/?i=11421 + [42] = https://curl.se/bug/?i=11606 + [43] = https://curl.se/bug/?i=11582 + [44] = https://curl.se/bug/?i=11563 + [45] = https://curl.se/bug/?i=11570 + [46] = https://curl.se/bug/?i=11581 + [47] = https://curl.se/bug/?i=11579 + [48] = https://curl.se/bug/?i=11568 + [49] = https://curl.se/bug/?i=11578 + [50] = https://curl.se/bug/?i=11635 + [51] = https://curl.se/bug/?i=11638 + [52] = https://curl.se/bug/?i=11621 + [53] = https://curl.se/bug/?i=10741 + [54] = https://curl.se/bug/?i=11655 + [55] = https://curl.se/bug/?i=11628 + [56] = https://curl.se/bug/?i=11592 + [57] = https://curl.se/bug/?i=11594 + [58] = https://curl.se/bug/?i=11597 + [59] = https://curl.se/bug/?i=11596 + [60] = https://curl.se/bug/?i=11681 + [61] = https://curl.se/bug/?i=11619 + [62] = https://curl.se/bug/?i=11631 + [63] = https://curl.se/bug/?i=11618 + [64] = https://curl.se/bug/?i=11666 + [65] = https://curl.se/bug/?i=11617 + [66] = https://curl.se/bug/?i=11616 + [67] = https://curl.se/bug/?i=11615 + [68] = https://curl.se/bug/?i=11612 + [69] = https://curl.se/bug/?i=11614 + [70] = https://curl.se/bug/?i=11611 + [71] = https://curl.se/bug/?i=11460 + [72] = https://curl.se/bug/?i=11661 + [73] = https://curl.se/bug/?i=11653 + [74] = https://curl.haxx.se/mail/lib-2018-10/0113.html + [75] = https://curl.se/bug/?i=11665 + [76] = https://curl.se/bug/?i=11651 + [77] = https://curl.se/bug/?i=11669 + [78] = https://curl.se/bug/?i=11645 + [79] = https://curl.se/bug/?i=1199308 + [80] = https://curl.se/bug/?i=11627 + [81] = https://curl.se/bug/?i=11624 + [82] = https://curl.se/bug/?i=11654 + [83] = https://curl.se/bug/?i=11705 + [84] = https://curl.se/bug/?i=11673 + [85] = https://curl.se/bug/?i=11752 + [86] = https://sourceforge.net/p/curl/bugs/440/ + [87] = https://curl.se/bug/?i=11657 + [88] = https://curl.se/bug/?i=11698 + [89] = https://curl.se/bug/?i=11697 + [90] = https://curl.se/bug/?i=11695 + [91] = https://curl.se/bug/?i=11728 + [92] = https://curl.se/bug/?i=11696 + [93] = https://curl.se/bug/?i=11727 + [94] = https://curl.se/bug/?i=11680 + [95] = https://curl.se/bug/?i=11650 + [96] = https://curl.se/bug/?i=11650 + [97] = https://curl.se/bug/?i=11687 + [98] = https://curl.se/bug/?i=11683 + [99] = https://curl.se/bug/?i=11675 + [100] = https://curl.se/bug/?i=11675 + [101] = https://curl.se/bug/?i=11686 + [102] = https://curl.se/bug/?i=11674 + [103] = https://curl.se/bug/?i=11685 + [104] = https://curl.se/bug/?i=11723 + [105] = https://curl.se/bug/?i=11713 + [106] = https://curl.se/bug/?i=11714 + [107] = https://curl.se/bug/?i=11739 + [108] = https://curl.se/bug/?i=11718 + [109] = https://curl.se/bug/?i=11678 + [110] = https://curl.se/bug/?i=11711 + [111] = https://curl.se/bug/?i=11722 + [112] = https://curl.se/bug/?i=11721 + [113] = https://curl.se/bug/?i=11737 + [114] = https://curl.se/bug/?i=11746 + [115] = https://curl.se/bug/?i=11710 + [116] = https://curl.se/bug/?i=11745 + [117] = https://curl.se/bug/?i=11749 + [118] = https://curl.se/bug/?i=11742 + [119] = https://curl.se/bug/?i=11530 + [120] = https://curl.se/bug/?i=11736 + [121] = https://curl.se/bug/?i=11576 + [122] = https://curl.se/bug/?i=11780 + [123] = https://curl.se/bug/?i=11734 + [124] = https://curl.se/bug/?i=11769 + [125] = https://curl.se/bug/?i=11766 + [126] = https://curl.se/bug/?i=11729 + [127] = https://curl.se/bug/?i=11794 + [128] = https://curl.se/bug/?i=11774 + [129] = https://curl.se/bug/?i=11773 + [130] = https://curl.se/bug/?i=11792 + [131] = https://curl.se/bug/?i=11771 + [132] = https://curl.se/bug/?i=11756 + [133] = https://curl.se/bug/?i=11791 + [134] = https://curl.se/bug/?i=11764 + [135] = https://curl.se/bug/?i=11490 + [136] = https://curl.se/bug/?i=11547 + [137] = https://curl.se/bug/?i=11779 + [138] = https://curl.se/bug/?i=11784 + [139] = https://curl.se/bug/?i=11785 + [140] = https://curl.se/bug/?i=11547 + [141] = https://curl.se/bug/?i=11738 + [142] = https://curl.se/bug/?i=11759 + [143] = https://curl.se/bug/?i=11747 + [144] = https://curl.se/bug/?i=11677 + [145] = https://curl.se/bug/?i=11735 + [146] = https://curl.se/bug/?i=11757 + [147] = https://curl.se/bug/?i=11693 + [148] = https://curl.se/bug/?i=11753 + [149] = https://curl.se/mail/lib-2023-08/0081.html + [150] = https://curl.se/bug/?i=11815 + [151] = https://curl.se/bug/?i=11800 + [152] = https://curl.se/bug/?i=11814 + [153] = https://curl.se/bug/?i=11825 + [154] = https://curl.se/bug/?i=11809 + [155] = https://curl.se/bug/?i=11822 + [156] = https://curl.se/bug/?i=11342 + [157] = https://curl.se/bug/?i=11801 + [158] = https://curl.se/bug/?i=11799 + [159] = https://curl.se/bug/?i=11664 + [160] = https://curl.se/bug/?i=11821 + [161] = https://curl.se/bug/?i=11821 + [162] = https://curl.se/bug/?i=11760 + [163] = https://curl.se/bug/?i=11632 + [164] = https://curl.se/bug/?i=11395 + [165] = https://curl.se/bug/?i=11839 + [166] = https://curl.se/bug/?i=11827 diff --git a/vendor/curl/include/curl/curl.h b/vendor/curl/include/curl/curl.h index 9443524216..898cbda839 100644 --- a/vendor/curl/include/curl/curl.h +++ b/vendor/curl/include/curl/curl.h @@ -93,7 +93,7 @@ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ - defined(__sun__) || defined(__serenity__) + defined(__sun__) || defined(__serenity__) || defined(__vxworks__) #include #endif @@ -161,7 +161,7 @@ typedef enum { CURLSSLBACKEND_GNUTLS = 2, CURLSSLBACKEND_NSS = 3, CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ - CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_GSKIT CURL_DEPRECATED(8.3.0, "") = 5, CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, CURLSSLBACKEND_WOLFSSL = 7, CURLSSLBACKEND_SCHANNEL = 8, @@ -781,7 +781,7 @@ typedef enum { CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT HTTP/1.0 */ CURLPROXY_HTTPS = 2, /* HTTPS but stick to HTTP/1 added in 7.52.0 */ - CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.1.0 */ + CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */ CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already in 7.10 */ CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ @@ -2113,7 +2113,7 @@ typedef enum { CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), /* allow RCPT TO command to fail for some recipients */ - CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290), + CURLOPT(CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOPTTYPE_LONG, 290), /* the private SSL-certificate as a "blob" */ CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291), @@ -2207,6 +2207,9 @@ typedef enum { /* Can leak things, gonna exit() soon */ CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322), + /* set a specific client IP for HAProxy PROXY protocol header? */ + CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2235,6 +2238,9 @@ typedef enum { /* */ #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT +/* Added in 8.2.0 */ +#define CURLOPT_MAIL_RCPT_ALLLOWFAILS CURLOPT_MAIL_RCPT_ALLOWFAILS + #else /* This is set if CURL_NO_OLDIES is defined at compile-time */ #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ @@ -2725,6 +2731,20 @@ CURL_EXTERN CURLcode curl_global_init_mem(long flags, */ CURL_EXTERN void curl_global_cleanup(void); +/* + * NAME curl_global_trace() + * + * DESCRIPTION + * + * curl_global_trace() can be invoked at application start to + * configure which components in curl should participate in tracing. + + * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the + * curl_version_info_data.features flag (fetch by curl_version_info()). + + */ +CURL_EXTERN CURLcode curl_global_trace(const char *config); + /* linked-list structure for the CURLOPT_QUOTE option (and other) */ struct curl_slist { char *data; @@ -2804,13 +2824,14 @@ CURL_EXTERN void curl_slist_free_all(struct curl_slist *list); */ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); -/* info about the certificate chain, only for OpenSSL, GnuTLS, Schannel, NSS - and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +/* info about the certificate chain, for SSL backends that support it. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ struct curl_certinfo { int num_of_certs; /* number of certificates with information */ struct curl_slist **certinfo; /* for each index in this array, there's a - linked list with textual information in the - format "name: value" */ + linked list with textual information for a + certificate in the format "name:content". + eg "Subject:foo", "Issuer:bar", etc. */ }; /* Information about the SSL library used and the respective internal SSL @@ -2918,7 +2939,9 @@ typedef enum { CURLINFO_REFERER = CURLINFO_STRING + 60, CURLINFO_CAINFO = CURLINFO_STRING + 61, CURLINFO_CAPATH = CURLINFO_STRING + 62, - CURLINFO_LASTONE = 62 + CURLINFO_XFER_ID = CURLINFO_OFF_T + 63, + CURLINFO_CONN_ID = CURLINFO_OFF_T + 64, + CURLINFO_LASTONE = 64 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as diff --git a/vendor/curl/include/curl/curlver.h b/vendor/curl/include/curl/curlver.h index f1382d0e3c..df93ef1274 100644 --- a/vendor/curl/include/curl/curlver.h +++ b/vendor/curl/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.1.2" +#define LIBCURL_VERSION "8.3.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 1 -#define LIBCURL_VERSION_PATCH 2 +#define LIBCURL_VERSION_MINOR 3 +#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 will @@ -59,7 +59,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 0x080102 +#define LIBCURL_VERSION_NUM 0x080300 /* * This is the date and time when the full source package was created. The @@ -70,7 +70,7 @@ * * "2007-11-23" */ -#define LIBCURL_TIMESTAMP "2023-05-30" +#define LIBCURL_TIMESTAMP "2023-09-13" #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) #define CURL_AT_LEAST_VERSION(x,y,z) \ diff --git a/vendor/curl/include/curl/mprintf.h b/vendor/curl/include/curl/mprintf.h index e652a6520e..dc5664bc53 100644 --- a/vendor/curl/include/curl/mprintf.h +++ b/vendor/curl/include/curl/mprintf.h @@ -32,18 +32,36 @@ extern "C" { #endif -CURL_EXTERN int curl_mprintf(const char *format, ...); -CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); -CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +#if (defined(__GNUC__) || defined(__clang__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) && !defined(CURL_NO_FMT_CHECKS) +#define CURL_TEMP_PRINTF(a,b) __attribute__ ((format(printf, a, b))) +#else +#define CURL_TEMP_PRINTF(a,b) +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, - const char *format, ...); -CURL_EXTERN int curl_mvprintf(const char *format, va_list args); -CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); -CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); + const char *format, ...) CURL_TEMP_PRINTF(3, 4); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, - const char *format, va_list args); -CURL_EXTERN char *curl_maprintf(const char *format, ...); -CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + const char *format, va_list args) + CURL_TEMP_PRINTF(3, 0); +CURL_EXTERN char *curl_maprintf(const char *format, ...) + CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); + +#undef CURL_TEMP_PRINTF #ifdef __cplusplus } /* end of extern "C" */ diff --git a/vendor/curl/include/curl/multi.h b/vendor/curl/include/curl/multi.h index 30a3d93017..13b55b7807 100644 --- a/vendor/curl/include/curl/multi.h +++ b/vendor/curl/include/curl/multi.h @@ -118,7 +118,7 @@ typedef struct CURLMsg CURLMsg; struct curl_waitfd { curl_socket_t fd; short events; - short revents; /* not supported yet */ + short revents; }; /* diff --git a/vendor/curl/include/curl/system.h b/vendor/curl/include/curl/system.h index def7739242..97e0d037a9 100644 --- a/vendor/curl/include/curl/system.h +++ b/vendor/curl/include/curl/system.h @@ -237,33 +237,28 @@ # define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__MVS__) -# if defined(__IBMC__) || defined(__IBMCPP__) -# if defined(_ILP32) -# elif defined(_LP64) -# endif -# if defined(_LONG_LONG) -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t -# define CURL_PULL_SYS_TYPES_H 1 -# define CURL_PULL_SYS_SOCKET_H 1 +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL # endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__370__) # if defined(__IBMC__) || defined(__IBMCPP__) @@ -352,6 +347,24 @@ # define CURL_PULL_SYS_TYPES_H 1 # define CURL_PULL_SYS_SOCKET_H 1 +#elif defined(__hpux) /* HP aCC compiler */ +# if !defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + /* ===================================== */ /* KEEP MSVC THE PENULTIMATE ENTRY */ /* ===================================== */ diff --git a/vendor/curl/include/curl/typecheck-gcc.h b/vendor/curl/include/curl/typecheck-gcc.h index bc8d7a78ce..b880f3dc60 100644 --- a/vendor/curl/include/curl/typecheck-gcc.h +++ b/vendor/curl/include/curl/typecheck-gcc.h @@ -280,6 +280,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ (option) == CURLOPT_FTPPORT || \ (option) == CURLOPT_HSTS || \ + (option) == CURLOPT_HAPROXY_CLIENT_IP || \ (option) == CURLOPT_INTERFACE || \ (option) == CURLOPT_ISSUERCERT || \ (option) == CURLOPT_KEYPASSWD || \ diff --git a/vendor/curl/include/curl/urlapi.h b/vendor/curl/include/curl/urlapi.h index b3504b683a..88cdeb3bca 100644 --- a/vendor/curl/include/curl/urlapi.h +++ b/vendor/curl/include/curl/urlapi.h @@ -96,7 +96,8 @@ typedef enum { #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the scheme is unknown. */ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */ +#define CURLU_PUNYCODE (1<<12) /* get the host name in punycode */ +#define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ typedef struct Curl_URL CURLU; diff --git a/vendor/curl/include/curl/websockets.h b/vendor/curl/include/curl/websockets.h index fd6a916547..6ef6a2bc92 100644 --- a/vendor/curl/include/curl/websockets.h +++ b/vendor/curl/include/curl/websockets.h @@ -54,13 +54,13 @@ struct curl_ws_frame { */ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *recv, - struct curl_ws_frame **metap); + const struct curl_ws_frame **metap); -/* sendflags for curl_ws_send() */ +/* flags for curl_ws_send() */ #define CURLWS_PONG (1<<6) /* - * NAME curl_easy_send() + * NAME curl_ws_send() * * DESCRIPTION * @@ -69,13 +69,13 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, */ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, size_t buflen, size_t *sent, - curl_off_t framesize, - unsigned int sendflags); + curl_off_t fragsize, + unsigned int flags); /* bits for the CURLOPT_WS_OPTIONS bitmask: */ #define CURLWS_RAW_MODE (1<<0) -CURL_EXTERN struct curl_ws_frame *curl_ws_meta(CURL *curl); +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); #ifdef __cplusplus } diff --git a/vendor/curl/lib/altsvc.c b/vendor/curl/lib/altsvc.c index f812bafc1b..22b0b69c77 100644 --- a/vendor/curl/lib/altsvc.c +++ b/vendor/curl/lib/altsvc.c @@ -38,6 +38,8 @@ #include "warnless.h" #include "fopen.h" #include "rename.h" +#include "strdup.h" +#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -97,19 +99,39 @@ static struct altsvc *altsvc_createid(const char *srchost, { struct altsvc *as = calloc(sizeof(struct altsvc), 1); size_t hlen; + size_t dlen; if(!as) return NULL; hlen = strlen(srchost); + dlen = strlen(dsthost); DEBUGASSERT(hlen); - as->src.host = strdup(srchost); + DEBUGASSERT(dlen); + if(!hlen || !dlen) + /* bad input */ + return NULL; + if((hlen > 2) && srchost[0] == '[') { + /* IPv6 address, strip off brackets */ + srchost++; + hlen -= 2; + } + else if(srchost[hlen - 1] == '.') + /* strip off trailing dot */ + hlen--; + if((dlen > 2) && dsthost[0] == '[') { + /* IPv6 address, strip off brackets */ + dsthost++; + dlen -= 2; + } + + as->src.host = Curl_memdup(srchost, hlen + 1); if(!as->src.host) goto error; - if(hlen && (srchost[hlen - 1] == '.')) - /* strip off trailing any dot */ - as->src.host[--hlen] = 0; - as->dst.host = strdup(dsthost); + as->src.host[hlen] = 0; + + as->dst.host = Curl_memdup(dsthost, dlen + 1); if(!as->dst.host) goto error; + as->dst.host[dlen] = 0; as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -231,18 +253,40 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) static CURLcode altsvc_out(struct altsvc *as, FILE *fp) { struct tm stamp; + const char *dst6_pre = ""; + const char *dst6_post = ""; + const char *src6_pre = ""; + const char *src6_post = ""; CURLcode result = Curl_gmtime(as->expires, &stamp); if(result) return result; - +#ifdef ENABLE_IPV6 + else { + char ipv6_unused[16]; + if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) { + dst6_pre = "["; + dst6_post = "]"; + } + if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) { + src6_pre = "["; + src6_post = "]"; + } + } +#endif fprintf(fp, - "%s %s %u " - "%s %s %u " + "%s %s%s%s %u " + "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " "%u %d\n", - Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, - Curl_alpnid2str(as->dst.alpnid), as->dst.host, as->dst.port, + Curl_alpnid2str(as->src.alpnid), + src6_pre, as->src.host, src6_post, + as->src.port, + + Curl_alpnid2str(as->dst.alpnid), + dst6_pre, as->dst.host, dst6_post, + as->dst.port, + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, as->persist, as->prio); @@ -424,7 +468,7 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, #ifdef DEBUGBUILD /* to play well with debug builds, we can *set* a fixed time this will return */ -static time_t debugtime(void *unused) +static time_t altsvc_debugtime(void *unused) { char *timestr = getenv("CURL_TIME"); (void)unused; @@ -434,7 +478,8 @@ static time_t debugtime(void *unused) } return time(NULL); } -#define time(x) debugtime(x) +#undef time +#define time(x) altsvc_debugtime(x) #endif #define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') @@ -499,9 +544,21 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, if(*p != ':') { /* host name starts here */ const char *hostp = p; - while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) - p++; - len = p - hostp; + if(*p == '[') { + /* pass all valid IPv6 letters - does not handle zone id */ + len = strspn(++p, "0123456789abcdefABCDEF:."); + if(p[len] != ']') + /* invalid host syntax, bail out */ + break; + /* we store the IPv6 numerical address *with* brackets */ + len += 2; + p = &p[len-1]; + } + else { + while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) + p++; + len = p - hostp; + } if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { infof(data, "Excessive alt-svc host name, ignoring."); valid = FALSE; diff --git a/vendor/curl/lib/amigaos.c b/vendor/curl/lib/amigaos.c index b0a9500267..139309b116 100644 --- a/vendor/curl/lib/amigaos.c +++ b/vendor/curl/lib/amigaos.c @@ -178,6 +178,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #endif /* CURLRES_AMIGA */ #ifdef USE_AMISSL +#include int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { diff --git a/vendor/curl/lib/amigaos.h b/vendor/curl/lib/amigaos.h index 7997ede80e..c99d963ece 100644 --- a/vendor/curl/lib/amigaos.h +++ b/vendor/curl/lib/amigaos.h @@ -39,4 +39,3 @@ void Curl_amiga_cleanup(void); #endif #endif /* HEADER_CURL_AMIGAOS_H */ - diff --git a/vendor/curl/lib/asyn-ares.c b/vendor/curl/lib/asyn-ares.c index 19fe8536b0..e73e41dab9 100644 --- a/vendor/curl/lib/asyn-ares.c +++ b/vendor/curl/lib/asyn-ares.c @@ -110,7 +110,7 @@ struct thread_data { }; /* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. + obtaining a "definitive" one. For old c-ares without getaddrinfo. This is intended to equal the c-ares default timeout. cURL always uses that default value. Unfortunately, c-ares doesn't expose its default timeout in @@ -120,6 +120,8 @@ struct thread_data { */ #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 +#define CARES_TIMEOUT_PER_ATTEMPT 2000 + /* * Curl_resolver_global_init() - the generic low-level asynchronous name * resolve API. Called from curl_global_init() to initialize global resolver @@ -173,6 +175,9 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) int optmask = ARES_OPT_SOCK_STATE_CB; options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = easy; + options.timeout = CARES_TIMEOUT_PER_ATTEMPT; + optmask |= ARES_OPT_TIMEOUTMS; + status = ares_init_options((ares_channel*)resolver, &options, optmask); if(status != ARES_SUCCESS) { if(status == ARES_ENOMEM) @@ -770,9 +775,14 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, int pf = PF_INET; memset(&hints, 0, sizeof(hints)); #ifdef CURLRES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && + Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } #endif /* CURLRES_IPV6 */ hints.ai_family = pf; hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? diff --git a/vendor/curl/lib/asyn-thread.c b/vendor/curl/lib/asyn-thread.c index 6f0a2126ab..a2e294f8f5 100644 --- a/vendor/curl/lib/asyn-thread.c +++ b/vendor/curl/lib/asyn-thread.c @@ -696,9 +696,13 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, *waitp = 0; /* default to synchronous response */ #ifdef CURLRES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } #endif /* CURLRES_IPV6 */ memset(&hints, 0, sizeof(hints)); diff --git a/vendor/curl/lib/base64.c b/vendor/curl/lib/base64.c index 971300e179..30db7e5889 100644 --- a/vendor/curl/lib/base64.c +++ b/vendor/curl/lib/base64.c @@ -32,18 +32,19 @@ !defined(CURL_DISABLE_POP3) || \ !defined(CURL_DISABLE_IMAP) || \ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) - -#include "urldata.h" /* for the Curl_easy definition */ +#include "curl/curl.h" #include "warnless.h" #include "curl_base64.h" /* The last 2 #include files should be in this order */ +#ifdef BUILDING_LIBCURL #include "curl_memory.h" +#endif #include "memdebug.h" /* ---- Base64 Encoding/Decoding Table --- */ /* Padding character string starts at offset 64. */ -static const char base64[]= +static const char base64encdec[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648 @@ -120,7 +121,7 @@ CURLcode Curl_base64_decode(const char *src, /* replaces { unsigned char c; - const unsigned char *p = (const unsigned char *)base64; + const unsigned char *p = (const unsigned char *)base64encdec; for(c = 0; *p; c++, p++) lookup[*p] = c; } @@ -264,7 +265,7 @@ static CURLcode base64_encode(const char *table64, CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { - return base64_encode(base64, inputbuff, insize, outptr, outlen); + return base64_encode(base64encdec, inputbuff, insize, outptr, outlen); } /* diff --git a/vendor/curl/lib/bufq.c b/vendor/curl/lib/bufq.c index 30598cf586..155544993d 100644 --- a/vendor/curl/lib/bufq.c +++ b/vendor/curl/lib/bufq.c @@ -418,7 +418,8 @@ ssize_t Curl_bufq_write(struct bufq *q, break; } n = chunk_append(tail, buf, len); - DEBUGASSERT(n); + if(!n) + break; nwritten += n; buf += n; len -= n; @@ -528,6 +529,14 @@ ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, } break; } + if(!chunk_written) { + if(!nwritten) { + /* treat as blocked */ + *err = CURLE_AGAIN; + nwritten = -1; + } + break; + } Curl_bufq_skip(q, (size_t)chunk_written); nwritten += chunk_written; } @@ -551,7 +560,8 @@ ssize_t Curl_bufq_write_pass(struct bufq *q, /* real error, fail */ return -1; } - /* would block */ + /* would block, bufq is full, give up */ + break; } } @@ -562,16 +572,25 @@ ssize_t Curl_bufq_write_pass(struct bufq *q, /* real error, fail */ return -1; } - /* no room in bufq, bail out */ - goto out; + /* no room in bufq */ + break; } + /* edge case of writer returning 0 (and len is >0) + * break or we might enter an infinite loop here */ + if(n == 0) + break; + /* Maybe only part of `data` has been added, continue to loop */ buf += (size_t)n; len -= (size_t)n; nwritten += (size_t)n; } -out: + if(!nwritten && len) { + *err = CURLE_AGAIN; + return -1; + } + *err = CURLE_OK; return nwritten; } diff --git a/vendor/curl/lib/c-hyper.c b/vendor/curl/lib/c-hyper.c index 756aebee21..61ca29a3ac 100644 --- a/vendor/curl/lib/c-hyper.c +++ b/vendor/curl/lib/c-hyper.c @@ -61,6 +61,11 @@ #include "curl_memory.h" #include "memdebug.h" +typedef enum { + USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */ + USERDATA_RESP_BODY +} userdata_t; + size_t Curl_hyper_recv(void *userp, hyper_context *ctx, uint8_t *buf, size_t buflen) { @@ -71,9 +76,11 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx, DEBUGASSERT(conn); (void)ctx; + DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen)); result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread); if(result == CURLE_AGAIN) { /* would block, register interest */ + DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen)); if(data->hyp.read_waker) hyper_waker_free(data->hyp.read_waker); data->hyp.read_waker = hyper_context_waker(ctx); @@ -87,6 +94,7 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx, failf(data, "Curl_read failed"); return HYPER_IO_ERROR; } + DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread)); return (size_t)nread; } @@ -98,8 +106,12 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, CURLcode result; ssize_t nwrote; + DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote); + if(!result && !nwrote) + result = CURLE_AGAIN; if(result == CURLE_AGAIN) { + DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); /* would block, register interest */ if(data->hyp.write_waker) hyper_waker_free(data->hyp.write_waker); @@ -114,6 +126,7 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, failf(data, "Curl_write failed"); return HYPER_IO_ERROR; } + DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote)); return (size_t)nwrote; } @@ -174,8 +187,11 @@ static int hyper_each_header(void *userdata, } } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; + result = Curl_bump_headersize(data, len, FALSE); + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } return HYPER_ITER_CONTINUE; } @@ -305,9 +321,8 @@ static CURLcode status_line(struct Curl_easy *data, if(result) return result; } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; - return CURLE_OK; + result = Curl_bump_headersize(data, len, FALSE); + return result; } /* @@ -340,7 +355,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, struct hyptransfer *h = &data->hyp; hyper_task *task; hyper_task *foreach; - hyper_error *hypererr = NULL; const uint8_t *reasonp; size_t reason_len; CURLcode result = CURLE_OK; @@ -383,19 +397,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, break; } t = hyper_task_type(task); - switch(t) { - case HYPER_TASK_ERROR: - hypererr = hyper_task_value(task); - break; - case HYPER_TASK_RESPONSE: - resp = hyper_task_value(task); - break; - default: - break; - } - hyper_task_free(task); - if(t == HYPER_TASK_ERROR) { + hyper_error *hypererr = hyper_task_value(task); + hyper_task_free(task); if(data->state.hresult) { /* override Hyper's view, might not even be an error */ result = data->state.hresult; @@ -406,37 +410,55 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); hyper_code code = hyper_error_code(hypererr); failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf); - if(code == HYPERE_ABORTED_BY_CALLBACK) + switch(code) { + case HYPERE_ABORTED_BY_CALLBACK: result = CURLE_OK; - else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount) - result = CURLE_GOT_NOTHING; - else if(code == HYPERE_INVALID_PEER_MESSAGE) + break; + case HYPERE_UNEXPECTED_EOF: + if(!data->req.bytecount) + result = CURLE_GOT_NOTHING; + else + result = CURLE_RECV_ERROR; + break; + case HYPERE_INVALID_PEER_MESSAGE: + /* bump headerbytecount to avoid the count remaining at zero and + appearing to not having read anything from the peer at all */ + data->req.headerbytecount++; result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */ - else + break; + default: result = CURLE_RECV_ERROR; + break; + } } *done = TRUE; hyper_error_free(hypererr); break; } - else if(h->endtask == task) { - /* end of transfer, forget the task handled, we might get a - * new one with the same address in the future. */ - *done = TRUE; - h->endtask = NULL; - infof(data, "hyperstream is done"); - if(!k->bodywrites) { - /* hyper doesn't always call the body write callback */ - bool stilldone; - result = Curl_http_firstwrite(data, data->conn, &stilldone); + else if(t == HYPER_TASK_EMPTY) { + void *userdata = hyper_task_userdata(task); + hyper_task_free(task); + if((userdata_t)userdata == USERDATA_RESP_BODY) { + /* end of transfer */ + *done = TRUE; + infof(data, "hyperstream is done"); + if(!k->bodywrites) { + /* hyper doesn't always call the body write callback */ + bool stilldone; + result = Curl_http_firstwrite(data, data->conn, &stilldone); + } + break; + } + else { + /* A background task for hyper; ignore */ + continue; } - break; - } - else if(t != HYPER_TASK_RESPONSE) { - *didwhat = KEEP_RECV; - break; } - /* HYPER_TASK_RESPONSE */ + + DEBUGASSERT(HYPER_TASK_RESPONSE); + + resp = hyper_task_value(task); + hyper_task_free(task); *didwhat = KEEP_RECV; if(!resp) { @@ -516,13 +538,12 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; break; } - DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY); + hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY); if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) { failf(data, "Couldn't hyper_executor_push the body-foreach"); result = CURLE_OUT_OF_MEMORY; break; } - h->endtask = foreach; hyper_response_free(resp); resp = NULL; @@ -750,7 +771,7 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, /* increasing the writebytecount here is a little premature but we don't know exactly when the body is sent */ data->req.writebytecount += fillcount; - Curl_pgrsSetUploadCounter(data, fillcount); + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); } return HYPER_POLL_READY; } @@ -787,15 +808,16 @@ static CURLcode bodysend(struct Curl_easy *data, hyper_body_set_data_func(body, uploadpostfields); else { result = Curl_get_upload_buffer(data); - if(result) + if(result) { + hyper_body_free(body); return result; + } /* init the "upload from here" pointer */ data->req.upload_fromhere = data->state.ulbuf; hyper_body_set_data_func(body, uploadstreamed); } if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { /* fail */ - hyper_body_free(body); result = CURLE_OUT_OF_MEMORY; } } @@ -1187,14 +1209,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = CURLE_OUT_OF_MEMORY; goto error; } + req = NULL; if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { failf(data, "Couldn't hyper_executor_push the send"); result = CURLE_OUT_OF_MEMORY; goto error; } + sendtask = NULL; /* ownership passed on */ hyper_clientconn_free(client); + client = NULL; if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { /* HTTP GET/HEAD download */ @@ -1207,8 +1232,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) the full request has been sent. */ data->req.start100 = Curl_now(); - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.proxyuserpwd); return CURLE_OK; @@ -1223,6 +1248,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(handshake) hyper_task_free(handshake); + if(client) + hyper_clientconn_free(client); + + if(req) + hyper_request_free(req); + return result; } diff --git a/vendor/curl/lib/c-hyper.h b/vendor/curl/lib/c-hyper.h index 4218cda75f..0c7de90b70 100644 --- a/vendor/curl/lib/c-hyper.h +++ b/vendor/curl/lib/c-hyper.h @@ -34,7 +34,6 @@ struct hyptransfer { hyper_waker *write_waker; hyper_waker *read_waker; const hyper_executor *exec; - hyper_task *endtask; hyper_waker *exp100_waker; hyper_waker *send_body_waker; }; diff --git a/vendor/curl/lib/cf-h1-proxy.c b/vendor/curl/lib/cf-h1-proxy.c index b42c4e6055..e9bc13d2a8 100644 --- a/vendor/curl/lib/cf-h1-proxy.c +++ b/vendor/curl/lib/cf-h1-proxy.c @@ -41,7 +41,7 @@ #include "cfilters.h" #include "cf-h1-proxy.h" #include "connect.h" -#include "curl_log.h" +#include "curl_trc.h" #include "curlx.h" #include "vtls/vtls.h" #include "transfer.h" @@ -54,16 +54,16 @@ typedef enum { - TUNNEL_INIT, /* init/default/no tunnel state */ - TUNNEL_CONNECT, /* CONNECT request is being send */ - TUNNEL_RECEIVE, /* CONNECT answer is being received */ - TUNNEL_RESPONSE, /* CONNECT response received completely */ - TUNNEL_ESTABLISHED, - TUNNEL_FAILED -} tunnel_state; + H1_TUNNEL_INIT, /* init/default/no tunnel state */ + H1_TUNNEL_CONNECT, /* CONNECT request is being send */ + H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ + H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H1_TUNNEL_ESTABLISHED, + H1_TUNNEL_FAILED +} h1_tunnel_state; /* struct for HTTP CONNECT tunneling */ -struct tunnel_state { +struct h1_tunnel_state { int sockindex; const char *hostname; int remote_port; @@ -78,23 +78,23 @@ struct tunnel_state { KEEPON_IGNORE } keepon; curl_off_t cl; /* size of content to read and ignore */ - tunnel_state tunnel_state; + h1_tunnel_state tunnel_state; BIT(chunked_encoding); BIT(close_connection); }; -static bool tunnel_is_established(struct tunnel_state *ts) +static bool tunnel_is_established(struct h1_tunnel_state *ts) { - return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED); + return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); } -static bool tunnel_is_failed(struct tunnel_state *ts) +static bool tunnel_is_failed(struct h1_tunnel_state *ts) { - return ts && (ts->tunnel_state == TUNNEL_FAILED); + return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); } -static CURLcode tunnel_reinit(struct tunnel_state *ts, +static CURLcode tunnel_reinit(struct h1_tunnel_state *ts, struct connectdata *conn, struct Curl_easy *data) { @@ -102,7 +102,7 @@ static CURLcode tunnel_reinit(struct tunnel_state *ts, DEBUGASSERT(ts); Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->req); - ts->tunnel_state = TUNNEL_INIT; + ts->tunnel_state = H1_TUNNEL_INIT; ts->keepon = KEEPON_CONNECT; ts->cl = 0; ts->close_connection = FALSE; @@ -124,12 +124,12 @@ static CURLcode tunnel_reinit(struct tunnel_state *ts, return CURLE_OK; } -static CURLcode tunnel_init(struct tunnel_state **pts, +static CURLcode tunnel_init(struct h1_tunnel_state **pts, struct Curl_easy *data, struct connectdata *conn, int sockindex) { - struct tunnel_state *ts; + struct h1_tunnel_state *ts; CURLcode result; if(conn->handler->flags & PROTOPT_NOTCPPROXY) { @@ -157,16 +157,16 @@ static CURLcode tunnel_init(struct tunnel_state **pts, return tunnel_reinit(ts, conn, data); } -static void tunnel_go_state(struct Curl_cfilter *cf, - struct tunnel_state *ts, - tunnel_state new_state, - struct Curl_easy *data) +static void h1_tunnel_go_state(struct Curl_cfilter *cf, + struct h1_tunnel_state *ts, + h1_tunnel_state new_state, + struct Curl_easy *data) { if(ts->tunnel_state == new_state) return; /* leaving this one */ switch(ts->tunnel_state) { - case TUNNEL_CONNECT: + case H1_TUNNEL_CONNECT: data->req.ignorebody = FALSE; break; default: @@ -174,37 +174,37 @@ static void tunnel_go_state(struct Curl_cfilter *cf, } /* entering this one */ switch(new_state) { - case TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); + case H1_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "new tunnel state 'init'"); tunnel_reinit(ts, cf->conn, data); break; - case TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); - ts->tunnel_state = TUNNEL_CONNECT; + case H1_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); + ts->tunnel_state = H1_TUNNEL_CONNECT; ts->keepon = KEEPON_CONNECT; Curl_dyn_reset(&ts->rcvbuf); break; - case TUNNEL_RECEIVE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'")); - ts->tunnel_state = TUNNEL_RECEIVE; + case H1_TUNNEL_RECEIVE: + CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); + ts->tunnel_state = H1_TUNNEL_RECEIVE; break; - case TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); - ts->tunnel_state = TUNNEL_RESPONSE; + case H1_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "new tunnel state 'response'"); + ts->tunnel_state = H1_TUNNEL_RESPONSE; break; - case TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); + case H1_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "new tunnel state 'established'"); infof(data, "CONNECT phase completed"); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; /* FALLTHROUGH */ - case TUNNEL_FAILED: - if(new_state == TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); + case H1_TUNNEL_FAILED: + if(new_state == H1_TUNNEL_FAILED) + CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); ts->tunnel_state = new_state; Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->req); @@ -225,9 +225,9 @@ static void tunnel_go_state(struct Curl_cfilter *cf, static void tunnel_free(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct tunnel_state *ts = cf->ctx; + struct h1_tunnel_state *ts = cf->ctx; if(ts) { - tunnel_go_state(cf, ts, TUNNEL_FAILED, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); Curl_dyn_free(&ts->rcvbuf); Curl_dyn_free(&ts->req); free(ts); @@ -270,7 +270,7 @@ static CURLcode CONNECT_host(struct Curl_easy *data, #ifndef USE_HYPER static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, - struct tunnel_state *ts) + struct h1_tunnel_state *ts) { struct connectdata *conn = cf->conn; char *hostheader = NULL; @@ -351,7 +351,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, static CURLcode send_CONNECT(struct Curl_easy *data, struct connectdata *conn, - struct tunnel_state *ts, + struct h1_tunnel_state *ts, bool *done) { struct SingleRequest *k = &data->req; @@ -399,7 +399,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data, static CURLcode on_resp_header(struct Curl_cfilter *cf, struct Curl_easy *data, - struct tunnel_state *ts, + struct h1_tunnel_state *ts, const char *header) { CURLcode result = CURLE_OK; @@ -416,7 +416,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, if(!auth) return CURLE_OUT_OF_MEMORY; - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header)); + CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); result = Curl_http_input_auth(data, proxy, auth); free(auth); @@ -475,7 +475,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, struct Curl_easy *data, - struct tunnel_state *ts, + struct h1_tunnel_state *ts, bool *done) { CURLcode result = CURLE_OK; @@ -587,7 +587,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return result; } - data->info.header_size += (long)perline; + result = Curl_bump_headersize(data, perline, TRUE); + if(result) + return result; /* Newlines are CRLF, so the CR is ignored as the line isn't really terminated until the LF comes. Treat a following CR @@ -636,7 +638,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* without content-length or chunked encoding, we can't keep the connection alive since the close is the end signal so we bail out at once instead */ - DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked")); + CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); ts->keepon = KEEPON_DONE; } } @@ -671,7 +673,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* The Hyper version of CONNECT */ static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, - struct tunnel_state *ts) + struct h1_tunnel_state *ts) { struct connectdata *conn = cf->conn; struct hyptransfer *h = &data->hyp; @@ -713,14 +715,13 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, } options = hyper_clientconn_options_new(); - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - if(!options) { failf(data, "Couldn't create hyper client options"); result = CURLE_OUT_OF_MEMORY; goto error; } + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); hyper_clientconn_options_exec(options, h->exec); @@ -751,6 +752,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, client = hyper_task_value(task); hyper_task_free(task); + req = hyper_request_new(); if(!req) { failf(data, "Couldn't hyper_request_new"); @@ -859,12 +861,17 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, result = CURLE_OUT_OF_MEMORY; goto error; } + req = NULL; if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { failf(data, "Couldn't hyper_executor_push the send"); result = CURLE_OUT_OF_MEMORY; goto error; } + sendtask = NULL; /* ownership passed on */ + + hyper_clientconn_free(client); + client = NULL; error: free(host); @@ -877,12 +884,15 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, hyper_task_free(handshake); if(client) hyper_clientconn_free(client); + if(req) + hyper_request_free(req); + return result; } static CURLcode send_CONNECT(struct Curl_easy *data, struct connectdata *conn, - struct tunnel_state *ts, + struct h1_tunnel_state *ts, bool *done) { struct hyptransfer *h = &data->hyp; @@ -919,7 +929,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data, static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, struct Curl_easy *data, - struct tunnel_state *ts, + struct h1_tunnel_state *ts, bool *done) { struct hyptransfer *h = &data->hyp; @@ -949,9 +959,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, #endif /* USE_HYPER */ -static CURLcode CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_state *ts) +static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) { struct connectdata *conn = cf->conn; CURLcode result; @@ -973,27 +983,27 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, } switch(ts->tunnel_state) { - case TUNNEL_INIT: + case H1_TUNNEL_INIT: /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start")); + CURL_TRC_CF(data, cf, "CONNECT start"); result = start_CONNECT(cf, data, ts); if(result) goto out; - tunnel_go_state(cf, ts, TUNNEL_CONNECT, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); /* FALLTHROUGH */ - case TUNNEL_CONNECT: + case H1_TUNNEL_CONNECT: /* see that the request is completely sent */ - DEBUGF(LOG_CF(data, cf, "CONNECT send")); + CURL_TRC_CF(data, cf, "CONNECT send"); result = send_CONNECT(data, cf->conn, ts, &done); if(result || !done) goto out; - tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); /* FALLTHROUGH */ - case TUNNEL_RECEIVE: + case H1_TUNNEL_RECEIVE: /* read what is there */ - DEBUGF(LOG_CF(data, cf, "CONNECT receive")); + CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; @@ -1003,11 +1013,11 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, if(result || !done) goto out; /* got it */ - tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); /* FALLTHROUGH */ - case TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "CONNECT response")); + case H1_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "CONNECT response"); if(data->req.newurl) { /* not the "final" response, we need to do a follow up request. * If the other side indicated a connection close, or if someone @@ -1019,7 +1029,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, * reset our tunnel state. To avoid recursion, we return * and expect to be called again. */ - DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open")); + CURL_TRC_CF(data, cf, "CONNECT need to close+open"); infof(data, "Connect me again please"); Curl_conn_cf_close(cf, data); connkeep(conn, "HTTP proxy CONNECT"); @@ -1028,7 +1038,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, } else { /* staying on this connection, reset state */ - tunnel_go_state(cf, ts, TUNNEL_INIT, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); } } break; @@ -1039,25 +1049,25 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, } while(data->req.newurl); - DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE); + 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. */ Curl_safefree(data->req.newurl); - /* failure, close this connection to avoid re-use */ + /* failure, close this connection to avoid reuse */ streamclose(conn, "proxy CONNECT failure"); - tunnel_go_state(cf, ts, TUNNEL_FAILED, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); return CURLE_RECV_ERROR; } /* 2xx response, SUCCESS! */ - tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); infof(data, "CONNECT tunnel established, response %d", data->info.httpproxycode); result = CURLE_OK; out: if(result) - tunnel_go_state(cf, ts, TUNNEL_FAILED, data); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); return result; } @@ -1066,15 +1076,15 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, bool blocking, bool *done) { CURLcode result; - struct tunnel_state *ts = cf->ctx; + struct h1_tunnel_state *ts = cf->ctx; if(cf->connected) { *done = TRUE; return CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "connect")); - result = cf->next->cft->connect(cf->next, data, blocking, done); + CURL_TRC_CF(data, cf, "connect"); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) return result; @@ -1089,7 +1099,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, /* TODO: can we do blocking? */ /* We want "seamless" operations through HTTP proxy tunnel */ - result = CONNECT(cf, data, ts); + result = H1_CONNECT(cf, data, ts); if(result) goto out; Curl_safefree(data->state.aptr.proxyuserpwd); @@ -1107,7 +1117,7 @@ static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { - struct tunnel_state *ts = cf->ctx; + struct h1_tunnel_state *ts = cf->ctx; int fds; fds = cf->next->cft->get_select_socks(cf->next, data, socks); @@ -1133,20 +1143,20 @@ static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); tunnel_free(cf, data); } static void cf_h1_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; if(cf->ctx) { - tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data); + h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); } if(cf->next) - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); } diff --git a/vendor/curl/lib/cf-h2-proxy.c b/vendor/curl/lib/cf-h2-proxy.c index 8e76ff84ab..a3feefca07 100644 --- a/vendor/curl/lib/cf-h2-proxy.c +++ b/vendor/curl/lib/cf-h2-proxy.c @@ -30,11 +30,12 @@ #include "urldata.h" #include "cfilters.h" #include "connect.h" -#include "curl_log.h" +#include "curl_trc.h" #include "bufq.h" #include "dynbuf.h" #include "dynhds.h" #include "http1.h" +#include "http2.h" #include "http_proxy.h" #include "multiif.h" #include "cf-h2-proxy.h" @@ -44,26 +45,25 @@ #include "curl_memory.h" #include "memdebug.h" -#define H2_NW_CHUNK_SIZE (128*1024) -#define H2_NW_RECV_CHUNKS 1 -#define H2_NW_SEND_CHUNKS 1 +#define PROXY_H2_CHUNK_SIZE (16*1024) -#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ +#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) +#define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) + +#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define PROXY_H2_NW_SEND_CHUNKS 1 + +#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE) -#define H2_TUNNEL_WINDOW_SIZE (1024 * 1024) -#define H2_TUNNEL_CHUNK_SIZE (32 * 1024) -#define H2_TUNNEL_RECV_CHUNKS \ - (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE) -#define H2_TUNNEL_SEND_CHUNKS \ - (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE) typedef enum { - TUNNEL_INIT, /* init/default/no tunnel state */ - TUNNEL_CONNECT, /* CONNECT request is being send */ - TUNNEL_RESPONSE, /* CONNECT response received completely */ - TUNNEL_ESTABLISHED, - TUNNEL_FAILED -} tunnel_state; + H2_TUNNEL_INIT, /* init/default/no tunnel state */ + H2_TUNNEL_CONNECT, /* CONNECT request is being send */ + H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H2_TUNNEL_ESTABLISHED, + H2_TUNNEL_FAILED +} h2_tunnel_state; struct tunnel_stream { struct http_resp *resp; @@ -72,10 +72,11 @@ struct tunnel_stream { char *authority; int32_t stream_id; uint32_t error; - tunnel_state state; - bool has_final_response; - bool closed; - bool reset; + size_t upload_blocked_len; + h2_tunnel_state state; + BIT(has_final_response); + BIT(closed); + BIT(reset); }; static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, @@ -85,11 +86,11 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, int port; bool ipv6_ip = cf->conn->bits.ipv6_ip; - ts->state = TUNNEL_INIT; + ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; - Curl_bufq_init2(&ts->recvbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, + Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - Curl_bufq_init(&ts->sendbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); + Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); if(cf->conn->bits.conn_to_host) hostname = cf->conn->conn_to_host.name; @@ -123,13 +124,13 @@ static void tunnel_stream_clear(struct tunnel_stream *ts) Curl_bufq_free(&ts->sendbuf); Curl_safefree(ts->authority); memset(ts, 0, sizeof(*ts)); - ts->state = TUNNEL_INIT; + ts->state = H2_TUNNEL_INIT; } -static void tunnel_go_state(struct Curl_cfilter *cf, - struct tunnel_stream *ts, - tunnel_state new_state, - struct Curl_easy *data) +static void h2_tunnel_go_state(struct Curl_cfilter *cf, + struct tunnel_stream *ts, + h2_tunnel_state new_state, + struct Curl_easy *data) { (void)cf; @@ -137,7 +138,7 @@ static void tunnel_go_state(struct Curl_cfilter *cf, return; /* leaving this one */ switch(ts->state) { - case TUNNEL_CONNECT: + case H2_TUNNEL_CONNECT: data->req.ignorebody = FALSE; break; default: @@ -145,30 +146,31 @@ static void tunnel_go_state(struct Curl_cfilter *cf, } /* entering this one */ switch(new_state) { - case TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); + case H2_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); tunnel_stream_clear(ts); break; - case TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); - ts->state = TUNNEL_CONNECT; + case H2_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id); + ts->state = H2_TUNNEL_CONNECT; break; - case TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); - ts->state = TUNNEL_RESPONSE; + case H2_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id); + ts->state = H2_TUNNEL_RESPONSE; break; - case TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); + case H2_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", + ts->stream_id); infof(data, "CONNECT phase completed"); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; /* FALLTHROUGH */ - case TUNNEL_FAILED: - if(new_state == TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); + case H2_TUNNEL_FAILED: + if(new_state == H2_TUNNEL_FAILED) + CURL_TRC_CF(data, cf, "[%d] 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 isn't accidentally used for the document request @@ -191,9 +193,11 @@ struct cf_h2_proxy_ctx { int32_t last_stream_id; BIT(conn_closed); BIT(goaway); + BIT(nw_out_blocked); }; /* How to access `call_data` from a cf_h2 filter */ +#undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) \ ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data @@ -219,35 +223,65 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) } } -static ssize_t nw_in_reader(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) +static void drain_tunnel(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *tunnel) +{ + unsigned char bits; + + (void)cf; + bits = CURL_CSELECT_IN; + if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) + bits |= CURL_CSELECT_OUT; + if(data->state.dselect_bits != bits || 1) { + CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x", + tunnel->stream_id, bits); + data->state.dselect_bits = bits; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + +static ssize_t proxy_nw_in_reader(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) { struct Curl_cfilter *cf = reader_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; - nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_in recv(len=%zu) -> %zd, %d", - buflen, nread, *err)); + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); + CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d", + buflen, nread, *err); + } + else { + nread = 0; + } return nread; } -static ssize_t nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, - CURLcode *err) +static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, + const unsigned char *buf, size_t buflen, + CURLcode *err) { struct Curl_cfilter *cf = writer_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_out send(len=%zu) -> %zd", buflen, nwritten)); + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, + err); + CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", + buflen, nwritten, *err); + } + else { + nwritten = 0; + } return nwritten; } -static int h2_client_new(struct Curl_cfilter *cf, - nghttp2_session_callbacks *cbs) +static int proxy_h2_client_new(struct Curl_cfilter *cf, + nghttp2_session_callbacks *cbs) { struct cf_h2_proxy_ctx *ctx = cf->ctx; nghttp2_option *o; @@ -271,15 +305,22 @@ static int h2_client_new(struct Curl_cfilter *cf, static ssize_t on_session_send(nghttp2_session *h2, const uint8_t *buf, size_t blen, int flags, void *userp); -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, +static int proxy_h2_on_frame_recv(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp); -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp); -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp); +#endif +static int proxy_h2_on_stream_close(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, void *userp); +static int proxy_h2_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp); static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *mem, size_t len, void *userp); @@ -298,8 +339,8 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->h2); memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); - Curl_bufq_init(&ctx->inbufq, H2_NW_CHUNK_SIZE, H2_NW_RECV_CHUNKS); - Curl_bufq_init(&ctx->outbufq, H2_NW_CHUNK_SIZE, H2_NW_SEND_CHUNKS); + 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)) goto out; @@ -311,14 +352,19 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, } nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); - nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); + nghttp2_session_callbacks_set_on_frame_recv_callback( + cbs, proxy_h2_on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif nghttp2_session_callbacks_set_on_data_chunk_recv_callback( cbs, tunnel_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); - nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); + nghttp2_session_callbacks_set_on_stream_close_callback( + cbs, proxy_h2_on_stream_close); + nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); /* The nghttp2 session is not yet setup, do it */ - rc = h2_client_new(cf, cbs); + rc = proxy_h2_client_new(cf, cbs); if(rc) { failf(data, "Couldn't initialize nghttp2"); goto out; @@ -343,7 +389,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, } rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); + PROXY_HTTP2_HUGE_WINDOW_SIZE); if(rc) { failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", nghttp2_strerror(rc), rc); @@ -358,31 +404,39 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, out: if(cbs) nghttp2_session_callbacks_del(cbs); - DEBUGF(LOG_CF(data, cf, "init proxy ctx -> %d", result)); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); return result; } -static CURLcode nw_out_flush(struct Curl_cfilter *cf, - struct Curl_easy *data) +static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) +{ + return !nghttp2_session_want_read(ctx->h2) && + !nghttp2_session_want_write(ctx->h2); +} + +static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - size_t buflen = Curl_bufq_len(&ctx->outbufq); ssize_t nwritten; CURLcode result; (void)data; - if(!buflen) + if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; - DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen)); - nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); + nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, + &result); if(nwritten < 0) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } return result; } - if((size_t)nwritten < buflen) { - return CURLE_AGAIN; - } - return CURLE_OK; + CURL_TRC_CF(data, cf, "[0] nw send buffer flushed"); + return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; } /* @@ -390,9 +444,9 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf, * This function returns 0 if it succeeds, or -1 and error code will * be assigned to *err. */ -static int h2_process_pending_input(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) { struct cf_h2_proxy_ctx *ctx = cf->ctx; const unsigned char *buf; @@ -402,8 +456,7 @@ static int h2_process_pending_input(struct Curl_cfilter *cf, while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); - DEBUGF(LOG_CF(data, cf, - "fed %zu bytes from nw to nghttp2 -> %zd", blen, rv)); + CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); if(rv < 0) { failf(data, "process_pending_input: nghttp2_session_mem_recv() returned " @@ -413,28 +466,20 @@ static int h2_process_pending_input(struct Curl_cfilter *cf, } Curl_bufq_skip(&ctx->inbufq, (size_t)rv); if(Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed")); + CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); break; } else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); } } - if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { - /* No more requests are allowed in the current session, so - the connection may not be reused. This is set when a - GOAWAY frame has been received or when the limit of stream - identifiers has been reached. */ - connclose(cf->conn, "http/2: No new requests allowed"); - } - return 0; } -static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_proxy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; @@ -442,9 +487,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, /* Process network input buffer fist */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); - if(h2_process_pending_input(cf, data, &result) < 0) + CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); + if(proxy_h2_process_pending_input(cf, data, &result) < 0) return result; } @@ -455,9 +500,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { - nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); + nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); + CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d", + Curl_bufq_len(&ctx->inbufq), nread, result); if(nread < 0) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data"); @@ -470,7 +515,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, break; } - if(h2_process_pending_input(cf, data, &result)) + if(proxy_h2_process_pending_input(cf, data, &result)) return result; } @@ -481,25 +526,22 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, return CURLE_OK; } -/* - * Check if there's been an update in the priority / - * dependency settings and if so it submits a PRIORITY frame with the updated - * info. - * Flush any out data pending in the network buffer. - */ -static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_proxy_ctx *ctx = cf->ctx; int rv = 0; - rv = nghttp2_session_send(ctx->h2); + ctx->nw_out_blocked = 0; + while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) + rv = nghttp2_session_send(ctx->h2); + if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); + CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); return CURLE_SEND_ERROR; } - return nw_out_flush(cf, data); + return proxy_h2_nw_out_flush(cf, data); } static ssize_t on_session_send(nghttp2_session *h2, @@ -517,7 +559,7 @@ static ssize_t on_session_send(nghttp2_session *h2, DEBUGASSERT(data); nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - nw_out_writer, cf, &result); + proxy_h2_nw_out_writer, cf, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { return NGHTTP2_ERR_WOULDBLOCK; @@ -532,8 +574,100 @@ static ssize_t on_session_send(nghttp2_session *h2, return nwritten; } -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +static int proxy_h2_on_frame_recv(nghttp2_session *session, + const nghttp2_frame *frame, + void *userp) { struct Curl_cfilter *cf = userp; struct cf_h2_proxy_ctx *ctx = cf->ctx; @@ -542,85 +676,85 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, (void)session; DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ DEBUGASSERT(data); switch(frame->hd.type) { case NGHTTP2_SETTINGS: - /* we do not do anything with this for now */ + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if((data->req.keepon & KEEP_SEND_HOLD) && + (data->req.keepon & KEEP_SEND)) { + data->req.keepon &= ~KEEP_SEND_HOLD; + drain_tunnel(cf, data, &ctx->tunnel); + CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", + stream_id); + } break; case NGHTTP2_GOAWAY: - infof(data, "recveived GOAWAY, error=%d, last_stream=%u", - frame->goaway.error_code, frame->goaway.last_stream_id); ctx->goaway = TRUE; break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE")); - break; default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); + break; } return 0; } if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] rcvd FRAME not for tunnel", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } switch(frame->hd.type) { - case NGHTTP2_DATA: - /* If body started on this stream, then receiving DATA is illegal. */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame DATA", stream_id)); - break; case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame HEADERS", stream_id)); - /* nghttp2 guarantees that :status is received, and we store it to stream->status_code. Fuzzing has proven this can still be reached without status code having been set. */ if(!ctx->tunnel.resp) return NGHTTP2_ERR_CALLBACK_FAILURE; /* Only final status code signals the end of header */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] got http status: %d", - stream_id, ctx->tunnel.resp->status)); + CURL_TRC_CF(data, cf, "[%d] got http status: %d", + stream_id, ctx->tunnel.resp->status); if(!ctx->tunnel.has_final_response) { if(ctx->tunnel.resp->status / 100 != 1) { ctx->tunnel.has_final_response = TRUE; } } break; - case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv RST", stream_id)); - ctx->tunnel.reset = TRUE; - break; case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id)); if((data->req.keepon & KEEP_SEND_HOLD) && (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; Curl_expire(data, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] unpausing after win update", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] unpausing after win update", + stream_id); } break; default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame %x", - stream_id, frame->hd.type)); break; } return 0; } -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp) +static int proxy_h2_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp) { struct Curl_cfilter *cf = userp; struct cf_h2_proxy_ctx *ctx = cf->ctx; @@ -633,10 +767,9 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, (void)session; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header for non-tunnel stream: " - "%.*s: %.*s", stream_id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: " + "%.*s: %.*s", stream_id, + (int)namelen, name, (int)valuelen, value); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -664,8 +797,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, return NGHTTP2_ERR_CALLBACK_FAILURE; resp->prev = ctx->tunnel.resp; ctx->tunnel.resp = resp; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] status: HTTP/2 %03d", - stream_id, ctx->tunnel.resp->status)); + CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", + stream_id, ctx->tunnel.resp->status); return 0; } @@ -678,10 +811,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header: %.*s: %.*s", - stream_id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", + stream_id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ } @@ -721,8 +852,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) *data_flags = NGHTTP2_DATA_FLAG_EOF; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] tunnel_send_callback -> %zd", - ts->stream_id, nread)); + CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", + ts->stream_id, nread); return nread; } @@ -752,8 +883,9 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, return 0; } -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp) +static int proxy_h2_on_stream_close(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, void *userp) { struct Curl_cfilter *cf = userp; struct cf_h2_proxy_ctx *ctx = cf->ctx; @@ -765,23 +897,23 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, if(stream_id != ctx->tunnel.stream_id) return 0; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] on_stream_close, %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); + CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); ctx->tunnel.closed = TRUE; ctx->tunnel.error = error_code; return 0; } -static CURLcode h2_submit(int32_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - nghttp2_session *h2, - struct httpreq *req, - const nghttp2_priority_spec *pri_spec, - void *stream_user_data, - nghttp2_data_source_read_callback read_callback, - void *read_ctx) +static CURLcode proxy_h2_submit(int32_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + nghttp2_session *h2, + struct httpreq *req, + const nghttp2_priority_spec *pri_spec, + void *stream_user_data, + nghttp2_data_source_read_callback read_callback, + void *read_ctx) { struct dynhds h2_headers; nghttp2_nv *nva = NULL; @@ -881,11 +1013,11 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, if(result) goto out; - result = h2_submit(&ts->stream_id, cf, data, ctx->h2, req, - NULL, ts, tunnel_send_callback, cf); + result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, + NULL, ts, tunnel_send_callback, cf); if(result) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(ts->stream_id), ts->stream_id)); + CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s", + ts->stream_id, nghttp2_strerror(ts->stream_id)); } out: @@ -907,7 +1039,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, DEBUGASSERT(ts->resp); if(ts->resp->status/100 == 2) { infof(data, "CONNECT tunnel established, response %d", ts->resp->status); - tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); return CURLE_OK; } @@ -919,8 +1051,8 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, } if(auth_reply) { - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", - auth_reply->value)); + 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) @@ -928,7 +1060,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, if(data->req.newurl) { /* Inidicator that we should try again */ Curl_safefree(data->req.newurl); - tunnel_go_state(cf, ts, TUNNEL_INIT, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); return CURLE_OK; } } @@ -937,9 +1069,9 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; } -static CURLcode CONNECT(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct tunnel_stream *ts) +static CURLcode H2_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct tunnel_stream *ts) { struct cf_h2_proxy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; @@ -948,27 +1080,27 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(ts->authority); do { switch(ts->state) { - case TUNNEL_INIT: + case H2_TUNNEL_INIT: /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority)); + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); result = submit_CONNECT(cf, data, ts); if(result) goto out; - tunnel_go_state(cf, ts, TUNNEL_CONNECT, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); /* FALLTHROUGH */ - case TUNNEL_CONNECT: + case H2_TUNNEL_CONNECT: /* see that the request is completely sent */ - result = h2_progress_ingress(cf, data); + result = proxy_h2_progress_ingress(cf, data); if(!result) - result = h2_progress_egress(cf, data); - if(result) { - tunnel_go_state(cf, ts, TUNNEL_FAILED, data); + result = proxy_h2_progress_egress(cf, data); + if(result && result != CURLE_AGAIN) { + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); break; } if(ts->has_final_response) { - tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); } else { result = CURLE_OK; @@ -976,28 +1108,28 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, } /* FALLTHROUGH */ - case TUNNEL_RESPONSE: + case H2_TUNNEL_RESPONSE: DEBUGASSERT(ts->has_final_response); result = inspect_response(cf, data, ts); if(result) goto out; break; - case TUNNEL_ESTABLISHED: + case H2_TUNNEL_ESTABLISHED: return CURLE_OK; - case TUNNEL_FAILED: + case H2_TUNNEL_FAILED: return CURLE_RECV_ERROR; default: break; } - } while(ts->state == TUNNEL_INIT); + } while(ts->state == H2_TUNNEL_INIT); out: if(result || ctx->tunnel.closed) - tunnel_go_state(cf, ts, TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); return result; } @@ -1043,10 +1175,10 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, /* for the secondary socket (FTP), use the "connect to host" * but ignore the "connect to port" (use the secondary port) */ - result = CONNECT(cf, data, ts); + result = H2_CONNECT(cf, data, ts); out: - *done = (result == CURLE_OK) && (ts->state == TUNNEL_ESTABLISHED); + *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); cf->connected = *done; CF_DATA_RESTORE(cf, save); return result; @@ -1082,7 +1214,7 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, { struct cf_h2_proxy_ctx *ctx = cf->ctx; if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx && ctx->tunnel.state == TUNNEL_ESTABLISHED && + (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED && !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) return TRUE; return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; @@ -1117,8 +1249,8 @@ static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, ssize_t rv = 0; if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " - "connection", ctx->tunnel.stream_id)); + CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " + "connection", ctx->tunnel.stream_id); connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; @@ -1138,7 +1270,8 @@ static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, *err = CURLE_OK; rv = 0; - DEBUGF(LOG_CF(data, cf, "handle_tunnel_close -> %zd, %d", rv, *err)); + CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d", + ctx->tunnel.stream_id, rv, *err); return rv; } @@ -1174,8 +1307,8 @@ static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "tunnel_recv(len=%zu) -> %zd, %d", - len, nread, *err)); + CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d", + ctx->tunnel.stream_id, len, nread, *err); return nread; } @@ -1188,14 +1321,14 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, struct cf_call_data save; CURLcode result; - if(ctx->tunnel.state != TUNNEL_ESTABLISHED) { + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { *err = CURLE_RECV_ERROR; return -1; } CF_DATA_SAVE(save, cf, data); if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { - *err = h2_progress_ingress(cf, data); + *err = proxy_h2_progress_ingress(cf, data); if(*err) goto out; } @@ -1203,117 +1336,241 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, nread = tunnel_recv(cf, data, buf, len, err); if(nread > 0) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] increase window by %zd", - ctx->tunnel.stream_id, nread)); + CURL_TRC_CF(data, cf, "[%d] increase window by %zd", + ctx->tunnel.stream_id, nread); nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); } - result = h2_progress_egress(cf, data); - if(result) { + result = proxy_h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) { + /* pending data to send, need to be called again. Ideally, we'd + * monitor the socket for POLLOUT, but we might not be in SENDING + * transfer state any longer and are unable to make this happen. + */ + CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN", + ctx->tunnel.stream_id); + drain_tunnel(cf, data, &ctx->tunnel); + } + else if(result) { *err = result; nread = -1; } out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d", - ctx->tunnel.stream_id, len, nread, *err)); + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + (nread >= 0 || *err == CURLE_AGAIN)) { + /* data pending and no fatal error to report. Need to trigger + * draining to avoid stalling when no socket events happen. */ + drain_tunnel(cf, data, &ctx->tunnel); + } + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d", + ctx->tunnel.stream_id, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, CURLcode *err) + const void *buf, size_t len, CURLcode *err) { struct cf_h2_proxy_ctx *ctx = cf->ctx; struct cf_call_data save; - ssize_t nwritten = -1; - const unsigned char *buf = mem; - size_t start_len = len; int rv; + ssize_t nwritten; + CURLcode result; + int blocked = 0; - if(ctx->tunnel.state != TUNNEL_ESTABLISHED) { + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { *err = CURLE_SEND_ERROR; return -1; } CF_DATA_SAVE(save, cf, data); - while(len) { + if(ctx->tunnel.closed) { + nwritten = -1; + *err = CURLE_SEND_ERROR; + goto out; + } + else if(ctx->tunnel.upload_blocked_len) { + /* the data in `buf` has already been submitted or added to the + * buffers, but have been EAGAINed on the last invocation. */ + DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len); + if(len < ctx->tunnel.upload_blocked_len) { + /* Did we get called again with a smaller `len`? This should not + * happen. We are not prepared to handle that. */ + failf(data, "HTTP/2 proxy, send again with decreased length"); + *err = CURLE_HTTP2; + nwritten = -1; + goto out; + } + nwritten = (ssize_t)ctx->tunnel.upload_blocked_len; + ctx->tunnel.upload_blocked_len = 0; + *err = CURLE_OK; + } + else { nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); - if(nwritten <= 0) { - if(*err && *err != CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "error adding data to tunnel sendbuf: %d", - *err)); - nwritten = -1; + if(nwritten < 0) { + if(*err != CURLE_AGAIN) goto out; - } - /* blocked */ nwritten = 0; } - else { - DEBUGASSERT((size_t)nwritten <= len); - buf += (size_t)nwritten; - len -= (size_t)nwritten; - } + } - /* resume the tunnel stream and let the h2 session send, which - * triggers reading from tunnel.sendbuf */ + if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + /* req body data is buffered, resume the potentially suspended stream */ rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } - *err = h2_progress_egress(cf, data); - if(*err) { - nwritten = -1; - goto out; - } + } - if(!nwritten && Curl_bufq_is_full(&ctx->tunnel.sendbuf)) { - size_t rwin; - /* we could not add to the buffer and after session processing, - * it is still full. */ - rwin = nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id); - DEBUGF(LOG_CF(data, cf, "cf_send: tunnel win %u/%zu", - nghttp2_session_get_remote_window_size(ctx->h2), rwin)); - if(rwin == 0) { - /* We cannot upload more as the stream's remote window size - * is 0. We need to receive WIN_UPDATEs before we can continue. - */ - data->req.keepon |= KEEP_SEND_HOLD; - DEBUGF(LOG_CF(data, cf, "pausing send as remote flow " - "window is exhausted")); - } - break; - } + result = proxy_h2_progress_ingress(cf, data); + if(result) { + *err = result; + nwritten = -1; + goto out; } - nwritten = start_len - len; - if(nwritten > 0) { - *err = CURLE_OK; + /* Call the nghttp2 send loop and flush to write ALL buffered data, + * headers and/or request body completely out to the network */ + result = proxy_h2_progress_egress(cf, data); + if(result == CURLE_AGAIN) { + blocked = 1; } - else if(ctx->tunnel.closed) { + else if(result) { + *err = result; nwritten = -1; - *err = CURLE_SEND_ERROR; + goto out; } - else { - nwritten = -1; + else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + /* although we wrote everything that nghttp2 wants to send now, + * there is data left in our stream send buffer unwritten. This may + * be due to the stream's HTTP/2 flow window being exhausted. */ + blocked = 1; + } + + if(blocked) { + /* Unable to send all data, due to connection blocked or H2 window + * exhaustion. Data is left in our stream buffer, or nghttp2's internal + * frame buffer or our network out buffer. */ + size_t rwin = nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id); + if(rwin == 0) { + /* H2 flow window exhaustion. + * FIXME: there is no way to HOLD all transfers that use this + * proxy connection AND to UNHOLD all of them again when the + * window increases. + * We *could* iterate over all data on this conn maybe? */ + CURL_TRC_CF(data, cf, "[%d] remote flow " + "window is exhausted", ctx->tunnel.stream_id); + } + + /* Whatever the cause, we need to return CURL_EAGAIN for this call. + * We have unwritten state that needs us being invoked again and EAGAIN + * is the only way to ensure that. */ + ctx->tunnel.upload_blocked_len = nwritten; + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " + "blocked_len=%zu", + ctx->tunnel.stream_id, len, + nghttp2_session_get_remote_window_size(ctx->h2), rwin, + nwritten); + drain_tunnel(cf, data, &ctx->tunnel); *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else if(proxy_h2_should_close_session(ctx)) { + /* nghttp2 thinks this session is done. If the stream has not been + * closed, this is an error state for out transfer */ + if(ctx->tunnel.closed) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + } + else { + CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); + *err = CURLE_HTTP2; + nwritten = -1; + } } out: - DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d ", - start_len, nwritten, *err)); + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + (nwritten >= 0 || *err == CURLE_AGAIN)) { + /* data pending and no fatal error to report. Need to trigger + * draining to avoid stalling when no socket events happen. */ + drain_tunnel(cf, data, &ctx->tunnel); + } + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", + ctx->tunnel.stream_id, len, nwritten, *err, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->tunnel.sendbuf), + Curl_bufq_len(&ctx->outbufq)); CF_DATA_RESTORE(cf, save); return nwritten; } +static bool proxy_h2_connisalive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + bool alive = TRUE; + + *input_pending = FALSE; + if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) + return FALSE; + + if(*input_pending) { + /* This happens before we've sent off a request and the connection is + not in use by any other transfer, there shouldn't be any data here, + only "protocol frames" */ + CURLcode result; + ssize_t nread = -1; + + *input_pending = FALSE; + nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); + if(nread != -1) { + if(proxy_h2_process_pending_input(cf, data, &result) < 0) + /* immediate error, considered dead */ + alive = FALSE; + else { + alive = !proxy_h2_should_close_session(ctx); + } + } + else if(result != CURLE_AGAIN) { + /* the read failed so let's say this is dead anyway */ + alive = FALSE; + } + } + + return alive; +} + +static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); + CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", + result, *input_pending); + CF_DATA_RESTORE(cf, save); + return result; +} + struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, cf_h2_proxy_close, @@ -1323,7 +1580,7 @@ struct Curl_cftype Curl_cft_h2_proxy = { cf_h2_proxy_send, cf_h2_proxy_recv, Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, + cf_h2_proxy_is_alive, Curl_cf_def_conn_keep_alive, Curl_cf_def_query, }; diff --git a/vendor/curl/lib/cf-haproxy.c b/vendor/curl/lib/cf-haproxy.c index 86d7fd1837..39ac415717 100644 --- a/vendor/curl/lib/cf-haproxy.c +++ b/vendor/curl/lib/cf-haproxy.c @@ -30,7 +30,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" -#include "curl_log.h" +#include "curl_trc.h" #include "multiif.h" /* The last 3 #include files should be in this order */ @@ -71,6 +71,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; const char *tcp_version; + const char *client_ip; DEBUGASSERT(ctx); DEBUGASSERT(ctx->state == HAPROXY_INIT); @@ -82,10 +83,14 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, #endif /* USE_UNIX_SOCKETS */ /* Emit the correct prefix for IPv6 */ tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; + if(data->set.str[STRING_HAPROXY_CLIENT_IP]) + client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + else + client_ip = data->info.conn_local_ip; result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", tcp_version, - data->info.conn_local_ip, + client_ip, data->info.conn_primary_ip, data->info.conn_local_port, data->info.conn_primary_port); @@ -110,7 +115,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, return CURLE_OK; } - result = cf->next->cft->connect(cf->next, data, blocking, done); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) return result; @@ -152,18 +157,18 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); cf_haproxy_ctx_free(cf->ctx); } static void cf_haproxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; cf_haproxy_ctx_reset(cf->ctx); if(cf->next) - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); } static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf, diff --git a/vendor/curl/lib/cf-https-connect.c b/vendor/curl/lib/cf-https-connect.c index d03cd1e0d5..be54aec740 100644 --- a/vendor/curl/lib/cf-https-connect.c +++ b/vendor/curl/lib/cf-https-connect.c @@ -28,7 +28,7 @@ #include "urldata.h" #include -#include "curl_log.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" #include "multiif.h" @@ -165,9 +165,9 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, if(winner != &ctx->h21_baller) cf_hc_baller_reset(&ctx->h21_baller, data); - DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, (int)Curl_timediff(Curl_now(), winner->started), - cf_hc_baller_reply_ms(winner, data))); + CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started), + cf_hc_baller_reply_ms(winner, data)); cf->next = winner->cf; winner->cf = NULL; @@ -218,16 +218,16 @@ static bool time_to_start_h21(struct Curl_cfilter *cf, elapsed_ms = Curl_timediff(now, ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21", - ctx->hard_eyeballs_timeout_ms)); + CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21", + ctx->hard_eyeballs_timeout_ms); return TRUE; } if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { - DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not " - "seen any data, starting h21", - ctx->soft_eyeballs_timeout_ms)); + CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not " + "seen any data, starting h21", + ctx->soft_eyeballs_timeout_ms); return TRUE; } /* set the effective hard timeout again */ @@ -258,7 +258,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->h3_baller.cf); DEBUGASSERT(!ctx->h21_baller.cf); DEBUGASSERT(!cf->next); - DEBUGF(LOG_CF(data, cf, "connect, init")); + CURL_TRC_CF(data, cf, "connect, init"); ctx->started = now; if(ctx->h3_baller.enabled) { cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); @@ -286,7 +286,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(cf_hc_baller_is_active(&ctx->h21_baller)) { - DEBUGF(LOG_CF(data, cf, "connect, check h21")); + CURL_TRC_CF(data, cf, "connect, check h21"); result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->h21_baller); @@ -297,7 +297,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { /* both failed or disabled. we give up */ - DEBUGF(LOG_CF(data, cf, "connect, all failed")); + CURL_TRC_CF(data, cf, "connect, all failed"); result = ctx->result = ctx->h3_baller.enabled? ctx->h3_baller.result : ctx->h21_baller.result; ctx->state = CF_HC_FAILURE; @@ -321,7 +321,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); return result; } @@ -345,7 +345,7 @@ static int cf_hc_get_select_socks(struct Curl_cfilter *cf, if(!cf_hc_baller_is_active(b)) continue; brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks); - DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc)); + CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc); if(!brc) continue; for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { @@ -359,7 +359,7 @@ static int cf_hc_get_select_socks(struct Curl_cfilter *cf, } } } - DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc)); + CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc); return rc; } @@ -371,14 +371,14 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf, if(cf->connected) return cf->next->cft->has_data_pending(cf->next, data); - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); return cf_hc_baller_data_pending(&ctx->h3_baller, data) || cf_hc_baller_data_pending(&ctx->h21_baller, data); } -static struct curltime get_max_baller_time(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query) +static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query) { struct cf_hc_ctx *ctx = cf->ctx; struct Curl_cfilter *cfb; @@ -408,12 +408,12 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_TIMER_CONNECT: { struct curltime *when = pres2; - *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); + *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); return CURLE_OK; } case CF_QUERY_TIMER_APPCONNECT: { struct curltime *when = pres2; - *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); + *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); return CURLE_OK; } default: @@ -427,12 +427,12 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf_hc_reset(cf, data); cf->connected = FALSE; if(cf->next) { - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); Curl_conn_cf_discard_chain(&cf->next, data); } } @@ -442,7 +442,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_hc_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); cf_hc_reset(cf, data); Curl_safefree(ctx); } @@ -450,7 +450,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, cf_hc_close, diff --git a/vendor/curl/lib/cf-socket.c b/vendor/curl/lib/cf-socket.c index 960979b025..effe6e649d 100644 --- a/vendor/curl/lib/cf-socket.c +++ b/vendor/curl/lib/cf-socket.c @@ -71,6 +71,7 @@ #include "warnless.h" #include "conncache.h" #include "multihandle.h" +#include "rand.h" #include "share.h" #include "version_win32.h" @@ -390,6 +391,7 @@ void Curl_sndbufset(curl_socket_t sockfd) } #endif +#ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sockfd, int af, unsigned int scope) { @@ -444,29 +446,24 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* interface */ if(!is_host) { #ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. + /* + * This binds the local socket to a particular interface. This will + * force even requests to other local interfaces to go out the external + * interface. Only bind to the interface when specified as interface, + * not just as a hostname or ip address. * - * interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try - * to use it straight away. + * The interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try to + * use it straight away. */ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, dev, (curl_socklen_t)strlen(dev) + 1) == 0) { - /* This is typically "errno 1, error: Operation not permitted" if - * you're not running as root or another suitable privileged - * user. - * If it succeeds it means the parameter was a valid interface and - * not an IP address. Return immediately. + /* This is often "errno 1, error: Operation not permitted" if you're + * not running as root or another suitable privileged user. If it + * succeeds it means the parameter was a valid interface and not an IP + * address. Return immediately. */ + infof(data, "socket successfully bound to interface '%s'", dev); return CURLE_OK; } #endif @@ -632,8 +629,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, port++; /* try next port */ if(port == 0) break; - infof(data, "Bind to local port %hu failed, trying next", port - 1); - /* We re-use/clobber the port variable here below */ + infof(data, "Bind to local port %d failed, trying next", port - 1); + /* We reuse/clobber the port variable here below */ if(sock->sa_family == AF_INET) si4->sin_port = ntohs(port); #ifdef ENABLE_IPV6 @@ -653,6 +650,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, return CURLE_INTERFACE_FAILED; } +#endif /* * verifyconnect() returns TRUE if the connect really has happened. @@ -727,8 +725,6 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) static CURLcode socket_connect_result(struct Curl_easy *data, const char *ipaddress, int error) { - char buffer[STRERROR_LEN]; - switch(error) { case EINPROGRESS: case EWOULDBLOCK: @@ -745,8 +741,15 @@ static CURLcode socket_connect_result(struct Curl_easy *data, default: /* unknown error, fallthrough and try another address! */ - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)ipaddress; +#else + { + char buffer[STRERROR_LEN]; + infof(data, "Immediate connect fail for %s: %s", + ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + } +#endif data->state.os_errno = error; /* connect failed */ return CURLE_COULDNT_CONNECT; @@ -775,6 +778,10 @@ struct cf_socket_ctx { struct curltime connected_at; /* when socket connected/got first byte */ struct curltime first_byte_at; /* when first byte was recvd */ int error; /* errno of last failure or 0 */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ + int wpartial_percent; /* percent of bytes written in send */ +#endif BIT(got_first_byte); /* if first byte was received */ BIT(accepted); /* socket was accepted, not connected */ BIT(active); @@ -790,6 +797,22 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, ctx->transport = transport; Curl_sock_assign_addr(&ctx->addr, ai, transport); Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS); +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_DBG_SOCK_WBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wblock_percent = (int)l; + } + p = getenv("CURL_DBG_SOCK_WPARTIAL"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wpartial_percent = (int)l; + } + } +#endif } struct reader_ctx { @@ -836,8 +859,8 @@ static ssize_t nw_in_read(void *reader_ctx, nread = -1; } } - DEBUGF(LOG_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", - len, (int)nread, *err)); + CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", + len, (int)nread, *err); return nread; } @@ -852,14 +875,14 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) * closed it) and we just forget about it. */ if(ctx->sock == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", active)", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ", active)", ctx->sock); socket_close(data, cf->conn, !ctx->accepted, ctx->sock); cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; } else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ") no longer at conn->sock[], discarding", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ") no longer at conn->sock[], discarding", ctx->sock); /* TODO: we do not want this to happen. Need to check which * code is messing with conn->sock[cf->sockindex] */ } @@ -869,9 +892,9 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) } else { /* this is our local socket, we did never publish it */ - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", not active)", ctx->sock)); - sclose(ctx->sock); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ", not active)", ctx->sock); + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); ctx->sock = CURL_SOCKET_BAD; } Curl_bufq_reset(&ctx->recvbuf); @@ -889,7 +912,7 @@ 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); - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); Curl_bufq_free(&ctx->recvbuf); free(ctx); cf->ctx = NULL; @@ -901,22 +924,26 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf, struct cf_socket_ctx *ctx = cf->ctx; #ifdef HAVE_GETSOCKNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssloc; - curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { + /* TFTP does not connect, so it cannot get the IP like this */ - memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { - int error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; - } - if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - ctx->l_ip, &ctx->l_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssloc; + curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + + memset(&ssloc, 0, sizeof(ssloc)); + if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + ctx->l_ip, &ctx->l_port)) { + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } } #else (void)data; @@ -953,7 +980,6 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, bool isconnected = FALSE; CURLcode result = CURLE_COULDNT_CONNECT; bool is_tcp; - const char *ipmsg; (void)data; DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); @@ -966,15 +992,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(result) goto out; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + const char *ipmsg; #ifdef ENABLE_IPV6 - if(ctx->addr.family == AF_INET6) { - set_ipv6_v6only(ctx->sock, 0); - ipmsg = " Trying [%s]:%d..."; + if(ctx->addr.family == AF_INET6) { + set_ipv6_v6only(ctx->sock, 0); + ipmsg = " Trying [%s]:%d..."; + } + else +#endif + ipmsg = " Trying %s:%d..."; + infof(data, ipmsg, ctx->r_ip, ctx->r_port); } - else #endif - ipmsg = " Trying %s:%d..."; - infof(data, ipmsg, ctx->r_ip, ctx->r_port); #ifdef ENABLE_IPV6 is_tcp = (ctx->addr.family == AF_INET @@ -1010,6 +1041,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } } +#ifndef CURL_DISABLE_BINDLOCAL /* possibly bind the local end to an IP, interface or port */ if(ctx->addr.family == AF_INET #ifdef ENABLE_IPV6 @@ -1027,6 +1059,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, goto out; } } +#endif /* set socket non-blocking */ (void)curlx_nonblock(ctx->sock, TRUE); @@ -1043,8 +1076,8 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, ctx->connected_at = Curl_now(); cf->connected = TRUE; } - DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, - result, ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, + result, ctx->sock); return result; } @@ -1151,7 +1184,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, rc = SOCKET_WRITABLE(ctx->sock, 0); if(rc == 0) { /* no connection yet */ - DEBUGF(LOG_CF(data, cf, "not connected yet")); + CURL_TRC_CF(data, cf, "not connected yet"); return CURLE_OK; } else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { @@ -1161,7 +1194,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, set_local_ip(cf, data); *done = TRUE; cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "connected")); + CURL_TRC_CF(data, cf, "connected"); return CURLE_OK; } } @@ -1241,11 +1274,34 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t nwritten; + size_t orig_len = len; *err = CURLE_OK; fdsave = cf->conn->sock[cf->sockindex]; cf->conn->sock[cf->sockindex] = ctx->sock; +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(ctx->wblock_percent > 0) { + unsigned char c; + Curl_rand(data, &c, 1); + if(c >= ((100-ctx->wblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); + *err = CURLE_AGAIN; + nwritten = -1; + cf->conn->sock[cf->sockindex] = fdsave; + return nwritten; + } + } + if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) { + len = len * ctx->wpartial_percent / 100; + if(!len) + len = 1; + CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes", + orig_len, len); + } +#endif + #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(cf->conn->bits.tcp_fastopen) { nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, @@ -1284,8 +1340,8 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, } } - DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d", - len, (int)nwritten, *err)); + CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", + orig_len, (int)nwritten, *err); cf->conn->sock[cf->sockindex] = fdsave; return nwritten; } @@ -1303,7 +1359,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, cf->conn->sock[cf->sockindex] = ctx->sock; if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) { - DEBUGF(LOG_CF(data, cf, "recv from buffer")); + CURL_TRC_CF(data, cf, "recv from buffer"); nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); } else { @@ -1320,7 +1376,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) { /* we have a partial read with an error. need to deliver * what we got, return the error later. */ - DEBUGF(LOG_CF(data, cf, "partial read: empty buffer first")); + CURL_TRC_CF(data, cf, "partial read: empty buffer first"); nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); } else if(nwritten < 0) { @@ -1333,7 +1389,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = 0; } else { - DEBUGF(LOG_CF(data, cf, "buffered %zd additional bytes", nwritten)); + CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten); nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); } } @@ -1343,8 +1399,8 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, - *err)); + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, + *err); if(nread > 0 && !ctx->got_first_byte) { ctx->first_byte_at = Curl_now(); ctx->got_first_byte = TRUE; @@ -1356,26 +1412,31 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, static void conn_set_primary_ip(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_socket_ctx *ctx = cf->ctx; #ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - int port; + struct cf_socket_ctx *ctx = cf->ctx; + if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { + /* TFTP does not connect the endpoint: getpeername() failed with errno + 107: Transport endpoint is not connected */ - plen = sizeof(ssrem); - memset(&ssrem, 0, plen); - if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - cf->conn->primary_ip, &port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + int port; + + plen = sizeof(ssrem); + memset(&ssrem, 0, plen); + if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + cf->conn->primary_ip, &port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } } #else cf->conn->primary_ip[0] = 0; @@ -1446,19 +1507,19 @@ static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, r = Curl_poll(pfd, 1, 0); if(r < 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead")); + CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead"); return FALSE; } else if(r == 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive")); + CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive"); return TRUE; } else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { - DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead")); + CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead"); return FALSE; } - DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive")); + CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive"); *input_pending = TRUE; return TRUE; } @@ -1511,7 +1572,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_tcp = { "TCP", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, cf_socket_close, @@ -1572,10 +1633,10 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, return socket_connect_result(data, ctx->r_ip, SOCKERRNO); } set_local_ip(cf, data); - DEBUGF(LOG_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T - " connected: [%s:%d] -> [%s:%d]", - (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", - ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port)); + CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T + " connected: [%s:%d] -> [%s:%d]", + (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", + ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port); (void)curlx_nonblock(ctx->sock, TRUE); switch(ctx->addr.family) { @@ -1615,7 +1676,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); if(result) { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); goto out; } @@ -1623,13 +1684,13 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, result = cf_udp_setup_quic(cf, data); if(result) goto out; - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (%s:%d)", - ctx->sock, ctx->l_ip, ctx->l_port)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (%s:%d)", + ctx->sock, ctx->l_ip, ctx->l_port); } else { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock); } *done = TRUE; cf->connected = TRUE; @@ -1641,7 +1702,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_udp = { "UDP", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_udp_connect, cf_socket_close, @@ -1692,7 +1753,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_cftype Curl_cft_unix = { "UNIX", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, cf_socket_close, @@ -1756,7 +1817,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_tcp_accept = { "TCP-ACCEPT", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_accept_connect, cf_socket_close, @@ -1801,8 +1862,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, ctx->active = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%" - CURL_FORMAT_SOCKET_T ")", ctx->sock)); + CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" + CURL_FORMAT_SOCKET_T ")", ctx->sock); out: if(result) { @@ -1866,9 +1927,9 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, ctx->accepted = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T - ", remote=%s port=%d)", - ctx->sock, ctx->r_ip, ctx->r_port)); + CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T + ", remote=%s port=%d)", + ctx->sock, ctx->r_ip, ctx->r_port); return CURLE_OK; } @@ -1913,4 +1974,3 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, } return CURLE_FAILED_INIT; } - diff --git a/vendor/curl/lib/cfilters.c b/vendor/curl/lib/cfilters.c index 291c823f3d..f74eb40039 100644 --- a/vendor/curl/lib/cfilters.c +++ b/vendor/curl/lib/cfilters.c @@ -50,7 +50,7 @@ void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) { cf->connected = FALSE; if(cf->next) - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); } #endif @@ -161,7 +161,7 @@ void Curl_conn_close(struct Curl_easy *data, int index) /* it is valid to call that without filters being present */ cf = data->conn->cfilter[index]; if(cf) { - cf->cft->close(cf, data); + cf->cft->do_close(cf, data); } } @@ -179,7 +179,7 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf, if(cf) { return cf->cft->do_recv(cf, data, buf, len, code); } - failf(data, CMSGI(data->conn, num, "recv: no filter connected")); + failf(data, "recv: no filter connected"); *code = CURLE_FAILED_INIT; return -1; } @@ -198,7 +198,7 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num, if(cf) { return cf->cft->do_send(cf, data, mem, len, code); } - failf(data, CMSGI(data->conn, num, "send: no filter connected")); + failf(data, "send: no filter connected"); DEBUGASSERT(0); *code = CURLE_FAILED_INIT; return -1; @@ -238,7 +238,7 @@ void Curl_conn_cf_add(struct Curl_easy *data, cf->conn = conn; cf->sockindex = index; conn->cfilter[index] = cf; - DEBUGF(LOG_CF(data, cf, "added")); + CURL_TRC_CF(data, cf, "added"); } void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, @@ -293,14 +293,14 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, bool blocking, bool *done) { if(cf) - return cf->cft->connect(cf, data, blocking, done); + return cf->cft->do_connect(cf, data, blocking, done); return CURLE_FAILED_INIT; } void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { if(cf) - cf->cft->close(cf, data); + cf->cft->do_close(cf, data); } int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, @@ -348,7 +348,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, *done = cf->connected; if(!*done) { - result = cf->cft->connect(cf, data, blocking, done); + result = cf->cft->do_connect(cf, data, blocking, done); if(!result && *done) { Curl_conn_ev_update_info(data, data->conn); conn_report_connect_stats(data, data->conn); @@ -646,4 +646,3 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, &n, NULL) : CURLE_UNKNOWN_OPTION; return (result || n <= 0)? 1 : (size_t)n; } - diff --git a/vendor/curl/lib/cfilters.h b/vendor/curl/lib/cfilters.h index 70dcbe758e..2c65264d98 100644 --- a/vendor/curl/lib/cfilters.h +++ b/vendor/curl/lib/cfilters.h @@ -168,8 +168,8 @@ struct Curl_cftype { int flags; /* flags of filter type */ int log_level; /* log level for such filters */ Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ - Curl_cft_connect *connect; /* establish connection */ - Curl_cft_close *close; /* close conn */ + Curl_cft_connect *do_connect; /* establish connection */ + Curl_cft_close *do_close; /* close conn */ Curl_cft_get_host *get_host; /* host filter talks to */ Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */ Curl_cft_data_pending *has_data_pending;/* conn has data pending */ diff --git a/vendor/curl/lib/conncache.h b/vendor/curl/lib/conncache.h index 959767d596..c60f8449ee 100644 --- a/vendor/curl/lib/conncache.h +++ b/vendor/curl/lib/conncache.h @@ -39,7 +39,8 @@ struct connectdata; struct conncache { struct Curl_hash hash; size_t num_conn; - long next_connection_id; + curl_off_t next_connection_id; + curl_off_t next_easy_id; struct curltime last_cleanup; /* handle used for closing cached connections */ struct Curl_easy *closure_handle; diff --git a/vendor/curl/lib/connect.c b/vendor/curl/lib/connect.c index ed5512138e..033fb7b17d 100644 --- a/vendor/curl/lib/connect.c +++ b/vendor/curl/lib/connect.c @@ -253,7 +253,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, } struct connfind { - long id_tofind; + curl_off_t id_tofind; struct connectdata *found; }; @@ -381,6 +381,11 @@ struct cf_he_ctx { struct curltime started; }; +/* when there are more than one IP address left to use, this macro returns how + much of the given timeout to spend on *this* attempt */ +#define TIMEOUT_LARGE 600 +#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms) + static CURLcode eyeballer_new(struct eyeballer **pballer, cf_ip_connect_create *cf_create, const struct Curl_addrinfo *addr, @@ -408,7 +413,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer, baller->primary = primary; baller->delay_ms = delay_ms; baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? - timeout_ms / 2 : timeout_ms; + USETIME(timeout_ms) : timeout_ms; baller->timeout_id = timeout_id; baller->result = CURLE_COULDNT_CONNECT; @@ -475,7 +480,7 @@ static void baller_initiate(struct Curl_cfilter *cf, out: if(result) { - DEBUGF(LOG_CF(data, cf, "%s failed", baller->name)); + CURL_TRC_CF(data, cf, "%s failed", baller->name); baller_close(baller, data); } if(cf_prev) @@ -501,7 +506,7 @@ static CURLcode baller_start(struct Curl_cfilter *cf, while(baller->addr) { baller->started = Curl_now(); baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? - timeoutms / 2 : timeoutms; + USETIME(timeoutms) : timeoutms; baller_initiate(cf, data, baller); if(!baller->result) break; @@ -601,8 +606,8 @@ static CURLcode is_connected(struct Curl_cfilter *cf, continue; } baller->result = baller_connect(cf, data, baller, &now, connected); - DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d", - baller->name, baller->result, *connected)); + CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d", + baller->name, baller->result, *connected); if(!baller->result) { if(*connected) { @@ -623,11 +628,11 @@ static CURLcode is_connected(struct Curl_cfilter *cf, } baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + CURL_TRC_CF(data, cf, "%s done", baller->name); } else { /* next attempt was started */ - DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name)); + CURL_TRC_CF(data, cf, "%s trying next", baller->name); ++ongoing; } } @@ -661,12 +666,12 @@ static CURLcode is_connected(struct Curl_cfilter *cf, Curl_timediff(now, ctx->started) >= baller->delay_ms) { baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + CURL_TRC_CF(data, cf, "%s done", baller->name); } else { - DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%" - CURL_FORMAT_TIMEDIFF_T "ms)", - baller->name, baller->timeoutms)); + CURL_TRC_CF(data, cf, "%s starting (timeout=%" + CURL_FORMAT_TIMEDIFF_T "ms)", + baller->name, baller->timeoutms); ++ongoing; ++added; } @@ -683,14 +688,14 @@ static CURLcode is_connected(struct Curl_cfilter *cf, } /* all ballers have failed to connect. */ - DEBUGF(LOG_CF(data, cf, "all eyeballers failed")); + CURL_TRC_CF(data, cf, "all eyeballers failed"); result = CURLE_COULDNT_CONNECT; for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { struct eyeballer *baller = ctx->baller[i]; - DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d", - baller?baller->name:NULL, - baller?baller->has_started:0, - baller?baller->result:0)); + CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", + baller?baller->name:NULL, + baller?baller->has_started:0, + baller?baller->result:0); if(baller && baller->has_started && baller->result) { result = baller->result; break; @@ -803,9 +808,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME); if(result) return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[0]->name, ctx->baller[0]->timeoutms)); + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[0]->name, ctx->baller[0]->timeoutms); if(addr1) { /* second one gets a delayed start */ result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, @@ -815,9 +820,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME2); if(result) return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[1]->name, ctx->baller[1]->timeoutms)); + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[1]->name, ctx->baller[1]->timeoutms); } Curl_expire(data, data->set.happy_eyeballs_timeout, @@ -931,13 +936,13 @@ static void cf_he_close(struct Curl_cfilter *cf, { struct cf_he_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf_he_ctx_clear(cf, data); cf->connected = FALSE; ctx->state = SCFST_INIT; if(cf->next) { - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); Curl_conn_cf_discard_chain(&cf->next, data); } } @@ -1007,7 +1012,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf, } } *pres1 = reply_ms; - DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1)); + CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1); return CURLE_OK; } case CF_QUERY_TIMER_CONNECT: { @@ -1034,7 +1039,7 @@ static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_he_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_he_ctx_clear(cf, data); } @@ -1045,7 +1050,7 @@ static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_happy_eyeballs = { "HAPPY-EYEBALLS", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_he_destroy, cf_he_connect, cf_he_close, @@ -1148,7 +1153,7 @@ static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, DEBUGASSERT(cf_at); cf_create = get_cf_create(transport); if(!cf_create) { - DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport)); + CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport); return CURLE_UNSUPPORTED_PROTOCOL; } result = cf_happy_eyeballs_create(&cf, data, cf_at->conn, @@ -1286,12 +1291,12 @@ static void cf_setup_close(struct Curl_cfilter *cf, { struct cf_setup_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; ctx->state = CF_SETUP_INIT; if(cf->next) { - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); Curl_conn_cf_discard_chain(&cf->next, data); } } @@ -1301,7 +1306,7 @@ static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_setup_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); Curl_safefree(ctx); } @@ -1309,7 +1314,7 @@ static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_setup = { "SETUP", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_setup_destroy, cf_setup_connect, cf_setup_close, @@ -1441,4 +1446,3 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, out: return result; } - diff --git a/vendor/curl/lib/cookie.c b/vendor/curl/lib/cookie.c index 0303efbb02..4345a84c6f 100644 --- a/vendor/curl/lib/cookie.c +++ b/vendor/curl/lib/cookie.c @@ -123,8 +123,9 @@ static void freecookie(struct Cookie *co) free(co); } -static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len, - const char *hostname) +static bool cookie_tailmatch(const char *cookie_domain, + size_t cookie_domain_len, + const char *hostname) { size_t hostname_len = strlen(hostname); @@ -696,7 +697,7 @@ Curl_cookie_add(struct Curl_easy *data, if(!domain || (is_ip && !strncmp(valuep, domain, vlen) && (vlen == strlen(domain))) - || (!is_ip && tailmatch(valuep, vlen, domain))) { + || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { strstore(&co->domain, valuep, vlen); if(!co->domain) { badcookie = TRUE; @@ -1431,7 +1432,7 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, /* now check if the domain is correct */ if(!co->domain || (co->tailmatch && !is_ip && - tailmatch(co->domain, strlen(co->domain), host)) || + cookie_tailmatch(co->domain, strlen(co->domain), host)) || ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { /* * the right part of the host matches the domain stuff in the diff --git a/vendor/curl/lib/curl_base64.h b/vendor/curl/lib/curl_base64.h index 806d4431cf..7f7cd1d98a 100644 --- a/vendor/curl/lib/curl_base64.h +++ b/vendor/curl/lib/curl_base64.h @@ -24,11 +24,18 @@ * ***************************************************************************/ +#ifndef BUILDING_LIBCURL +/* this renames functions so that the tool code can use the same code + without getting symbol collisions */ +#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d) +#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d) +#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c) +#endif + CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); CURLcode Curl_base64_decode(const char *src, unsigned char **outptr, size_t *outlen); - #endif /* HEADER_CURL_BASE64_H */ diff --git a/vendor/curl/lib/curl_des.c b/vendor/curl/lib/curl_des.c index 5c623b35bc..b77763f268 100644 --- a/vendor/curl/lib/curl_des.c +++ b/vendor/curl/lib/curl_des.c @@ -24,12 +24,11 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) #include "curl_des.h" diff --git a/vendor/curl/lib/curl_des.h b/vendor/curl/lib/curl_des.h index 6ec450accc..66525ab436 100644 --- a/vendor/curl/lib/curl_des.h +++ b/vendor/curl/lib/curl_des.h @@ -26,12 +26,11 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) /* Applies odd parity to the given byte array */ void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); diff --git a/vendor/curl/lib/curl_hmac.h b/vendor/curl/lib/curl_hmac.h index 11625c0cb4..9438ca782a 100644 --- a/vendor/curl/lib/curl_hmac.h +++ b/vendor/curl/lib/curl_hmac.h @@ -24,7 +24,8 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) #include diff --git a/vendor/curl/lib/curl_log.h b/vendor/curl/lib/curl_log.h deleted file mode 100644 index ad6143fa99..0000000000 --- a/vendor/curl/lib/curl_log.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef HEADER_CURL_LOG_H -#define HEADER_CURL_LOG_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; -struct Curl_cfilter; - -/** - * Init logging, return != 0 on failure. - */ -CURLcode Curl_log_init(void); - - -void Curl_infof(struct Curl_easy *, const char *fmt, ...); -void Curl_failf(struct Curl_easy *, const char *fmt, ...); - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif - -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define infof Curl_infof - -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define failf Curl_failf - - -#define CURL_LOG_DEFAULT 0 -#define CURL_LOG_DEBUG 1 -#define CURL_LOG_TRACE 2 - - -/* the function used to output verbose information */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size); - -#ifdef DEBUGBUILD - -/* explainer: we have some mix configuration and werror settings - * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced - * on gnuc and some other compiler. Need to treat carefully. - */ -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - -#define LOG_CF(data, cf, ...) \ - do { if(Curl_log_cf_is_debug(cf)) \ - Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0) -#else -#define LOG_CF Curl_log_cf_debug -#endif - -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 3, 4))); -#else - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(cf) \ - ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG) - -#else /* !DEBUGBUILD */ - -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(...) Curl_nop_stmt -#define Curl_log_cf_debug(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(x...) Curl_nop_stmt -#define Curl_log_cf_debug(x...) Curl_nop_stmt -#else -#define LOG_CF Curl_log_cf_debug -/* without c99, we seem unable to completely define away this function. */ -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(x) ((void)(x), FALSE) - -#endif /* !DEBUGBUILD */ - -#define LOG_CF_IS_DEBUG(x) Curl_log_cf_is_debug(x) - -/* Macros intended for DEBUGF logging, use like: - * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much")); - * and it will output: - * [CONN-1-0][CF-SSL] this filter very much rocks - * on connection #1 with sockindex 0 for filter of type "SSL". */ -#define DMSG(d,msg) \ - "[CONN-%ld] "msg, (d)->conn->connection_id -#define DMSGI(d,i,msg) \ - "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i) -#define CMSG(c,msg) \ - "[CONN-%ld] "msg, (c)->connection_id -#define CMSGI(c,i,msg) \ - "[CONN-%ld-%d] "msg, (c)->connection_id, (i) -#define CFMSG(cf,msg) \ - "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \ - (cf)->sockindex, (cf)->cft->name - - - -#endif /* HEADER_CURL_LOG_H */ diff --git a/vendor/curl/lib/curl_md4.h b/vendor/curl/lib/curl_md4.h index 03567b9916..4706e49578 100644 --- a/vendor/curl/lib/curl_md4.h +++ b/vendor/curl/lib/curl_md4.h @@ -25,14 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" +#include -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if defined(USE_CURL_NTLM_CORE) #define MD4_DIGEST_LENGTH 16 -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len); +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len); -#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */ +#endif /* defined(USE_CURL_NTLM_CORE) */ #endif /* HEADER_CURL_MD4_H */ diff --git a/vendor/curl/lib/curl_md5.h b/vendor/curl/lib/curl_md5.h index ec2512f002..61671c306a 100644 --- a/vendor/curl/lib/curl_md5.h +++ b/vendor/curl/lib/curl_md5.h @@ -24,7 +24,9 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) + #include "curl_hmac.h" #define MD5_DIGEST_LEN 16 diff --git a/vendor/curl/lib/curl_memory.h b/vendor/curl/lib/curl_memory.h index 1a21c5ad4a..b8c46d7939 100644 --- a/vendor/curl/lib/curl_memory.h +++ b/vendor/curl/lib/curl_memory.h @@ -55,9 +55,65 @@ */ #ifdef HEADER_CURL_MEMDEBUG_H -#error "Header memdebug.h shall not be included before curl_memory.h" +/* cleanup after memdebug.h */ + +#ifdef MEMDEBUG_NODEFINES +#ifdef CURLDEBUG + +#undef strdup +#undef malloc +#undef calloc +#undef realloc +#undef free +#undef send +#undef recv + +#ifdef WIN32 +# ifdef UNICODE +# undef wcsdup +# undef _wcsdup +# undef _tcsdup +# else +# undef _tcsdup +# endif +#endif + +#undef socket +#undef accept +#ifdef HAVE_SOCKETPAIR +#undef socketpair #endif +#ifdef HAVE_GETADDRINFO +#if defined(getaddrinfo) && defined(__osf__) +#undef ogetaddrinfo +#else +#undef getaddrinfo +#endif +#endif /* HAVE_GETADDRINFO */ + +#ifdef HAVE_FREEADDRINFO +#undef freeaddrinfo +#endif /* HAVE_FREEADDRINFO */ + +/* sclose is probably already defined, redefine it! */ +#undef sclose +#undef fopen +#undef fdopen +#undef fclose + +#endif /* MEMDEBUG_NODEFINES */ +#endif /* CURLDEBUG */ + +#undef HEADER_CURL_MEMDEBUG_H +#endif /* HEADER_CURL_MEMDEBUG_H */ + +/* +** Following section applies even when CURLDEBUG is not defined. +*/ + +#undef fake_sclose + #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */ /* * The following memory function replacement typedef's are COPIED from diff --git a/vendor/curl/lib/curl_ntlm_core.c b/vendor/curl/lib/curl_ntlm_core.c index ba8457d6ea..cc0ed91672 100644 --- a/vendor/curl/lib/curl_ntlm_core.c +++ b/vendor/curl/lib/curl_ntlm_core.c @@ -38,7 +38,7 @@ 1. USE_OPENSSL 2. USE_WOLFSSL 3. USE_GNUTLS - 4. USE_NSS + 4. - 5. USE_MBEDTLS 6. USE_SECTRANSP 7. USE_OS400CRYPTO @@ -47,7 +47,7 @@ This ensures that: - the same SSL branch gets activated throughout this source file even if multiple backends are enabled at the same time. - - OpenSSL and NSS have higher priority than Windows Crypt, due + - OpenSSL has higher priority than Windows Crypt, due to issues with the latter supporting NTLM2Session responses in NTLM type-3 messages. */ @@ -96,12 +96,6 @@ # include -#elif defined(USE_NSS) - -# include -# include -# include - #elif defined(USE_MBEDTLS) # include @@ -188,70 +182,6 @@ static void setup_des_key(const unsigned char *key_56, des_set_key(des, (const uint8_t *) key); } -#elif defined(USE_NSS) - -/* - * encrypt_des() expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of - * data, using the expanded key. IN should point to 64 bits of source data, - * OUT to a 64 bit output buffer. - */ -static bool encrypt_des(const unsigned char *in, unsigned char *out, - const unsigned char *key_56) -{ - const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */ - char key[8]; /* expanded 64 bit key */ - SECItem key_item; - PK11SymKey *symkey = NULL; - SECItem *param = NULL; - PK11Context *ctx = NULL; - int out_len; /* not used, required by NSS */ - bool rv = FALSE; - - /* use internal slot for DES encryption (requires NSS to be initialized) */ - PK11SlotInfo *slot = PK11_GetInternalKeySlot(); - if(!slot) - return FALSE; - - /* Expand the 56-bit key to 64-bits */ - extend_key_56_to_64(key_56, key); - - /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); - - /* Import the key */ - key_item.data = (unsigned char *)key; - key_item.len = sizeof(key); - symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT, - &key_item, NULL); - if(!symkey) - goto fail; - - /* Create the DES encryption context */ - param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL); - if(!param) - goto fail; - ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param); - if(!ctx) - goto fail; - - /* Perform the encryption */ - if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8, - (unsigned char *)in, /* inbuflen */ 8) - && SECSuccess == PK11_Finalize(ctx)) - rv = /* all OK */ TRUE; - -fail: - /* cleanup */ - if(ctx) - PK11_DestroyContext(ctx, PR_TRUE); - if(symkey) - PK11_FreeSymKey(symkey); - if(param) - SECITEM_FreeItem(param, PR_TRUE); - PK11_FreeSlot(slot); - return rv; -} - #elif defined(USE_MBEDTLS) static bool encrypt_des(const unsigned char *in, unsigned char *out, @@ -402,7 +332,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, des_encrypt(&des, 8, results + 8, plaintext); setup_des_key(keys + 14, &des); des_encrypt(&des, 8, results + 16, plaintext); -#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); @@ -444,7 +374,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); @@ -489,6 +419,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, { size_t len = strlen(password); unsigned char *pw; + CURLcode result; if(len > SIZE_T_MAX/2) /* avoid integer overflow */ return CURLE_OUT_OF_MEMORY; pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); @@ -498,12 +429,13 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, ascii_to_unicode_le(pw, password, len); /* Create NT hashed password. */ - Curl_md4it(ntbuffer, pw, 2 * len); - memset(ntbuffer + 16, 0, 21 - 16); + result = Curl_md4it(ntbuffer, pw, 2 * len); + if(!result) + memset(ntbuffer + 16, 0, 21 - 16); free(pw); - return CURLE_OK; + return result; } #if !defined(USE_WINDOWS_SSPI) diff --git a/vendor/curl/lib/curl_ntlm_core.h b/vendor/curl/lib/curl_ntlm_core.h index 33b651f5ea..0c62ee052a 100644 --- a/vendor/curl/lib/curl_ntlm_core.h +++ b/vendor/curl/lib/curl_ntlm_core.h @@ -28,15 +28,6 @@ #if defined(USE_CURL_NTLM_CORE) -/* If NSS is the first available SSL backend (see order in curl_ntlm_core.c) - then it must be initialized to be used by NTLM. */ -#if !defined(USE_OPENSSL) && \ - !defined(USE_WOLFSSL) && \ - !defined(USE_GNUTLS) && \ - defined(USE_NSS) -#define NTLM_NEEDS_NSS_INIT -#endif - #if defined(USE_OPENSSL) # include #elif defined(USE_WOLFSSL) diff --git a/vendor/curl/lib/curl_printf.h b/vendor/curl/lib/curl_printf.h index 6d3d492cc4..46ef344f76 100644 --- a/vendor/curl/lib/curl_printf.h +++ b/vendor/curl/lib/curl_printf.h @@ -37,6 +37,7 @@ # undef vprintf # undef vfprintf # undef vsnprintf +# undef mvsnprintf # undef aprintf # undef vaprintf # define printf curl_mprintf diff --git a/vendor/curl/lib/curl_sasl.c b/vendor/curl/lib/curl_sasl.c index 119fb9b25a..91ddf10622 100644 --- a/vendor/curl/lib/curl_sasl.c +++ b/vendor/curl/lib/curl_sasl.c @@ -221,12 +221,12 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, } /* - * state() + * sasl_state() * * This is the ONLY way to change SASL state! */ -static void state(struct SASL *sasl, struct Curl_easy *data, - saslstate newstate) +static void sasl_state(struct SASL *sasl, struct Curl_easy *data, + saslstate newstate) { #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) /* for debug purposes */ @@ -420,7 +420,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if((enabledmechs & SASL_MECH_DIGEST_MD5) && Curl_auth_is_digest_supported()) { mech = SASL_MECH_STRING_DIGEST_MD5; @@ -508,7 +508,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, if(!result) { *progress = SASL_INPROGRESS; - state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); + sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); } } @@ -530,8 +530,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; const char *hostname, *disp_hostname; int port; -#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_NTLM) +#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; @@ -548,14 +548,14 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(code != sasl->params->finalcode) result = CURLE_LOGIN_DENIED; *progress = SASL_DONE; - state(sasl, data, SASL_STOP); + sasl_state(sasl, data, SASL_STOP); return result; } if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && code != sasl->params->contcode) { *progress = SASL_DONE; - state(sasl, data, SASL_STOP); + sasl_state(sasl, data, SASL_STOP); return CURLE_LOGIN_DENIED; } @@ -577,7 +577,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_EXTERNAL: result = Curl_auth_create_external_message(conn->user, &resp); break; -#ifndef CURL_DISABLE_CRYPTO_AUTH #ifdef USE_GSASL case SASL_GSASL: result = get_server_message(sasl, data, &serverdata); @@ -587,6 +586,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, newstate = SASL_GSASL; break; #endif +#ifndef CURL_DISABLE_DIGEST_AUTH case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) @@ -698,7 +698,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(code == sasl->params->finalcode) { /* Final response was received so we are done */ *progress = SASL_DONE; - state(sasl, data, SASL_STOP); + sasl_state(sasl, data, SASL_STOP); return result; } else if(code == sasl->params->contcode) { @@ -708,7 +708,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else { *progress = SASL_DONE; - state(sasl, data, SASL_STOP); + sasl_state(sasl, data, SASL_STOP); return CURLE_LOGIN_DENIED; } @@ -745,7 +745,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, Curl_bufref_free(&resp); - state(sasl, data, newstate); + sasl_state(sasl, data, newstate); return result; } diff --git a/vendor/curl/lib/curl_setup.h b/vendor/curl/lib/curl_setup.h index 38cf6ffc12..b43714da74 100644 --- a/vendor/curl/lib/curl_setup.h +++ b/vendor/curl/lib/curl_setup.h @@ -258,8 +258,9 @@ #if defined(__APPLE__) && !defined(USE_ARES) #include #define USE_RESOLVE_ON_IPS 1 -# if defined(TARGET_OS_OSX) && TARGET_OS_OSX -# define CURL_OSX_CALL_COPYPROXIES 1 +# if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \ + defined(ENABLE_IPV6) +# define CURL_MACOS_CALL_COPYPROXIES 1 # endif #endif @@ -298,6 +299,7 @@ # if defined(HAVE_PROTO_BSDSOCKET_H) && \ (!defined(__amigaos4__) || defined(USE_AMISSL)) /* use bsdsocket.library directly, instead of libc networking functions */ +# define _SYS_MBUF_H /* m_len define clashes with curl */ # include # ifdef __amigaos4__ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, @@ -643,32 +645,30 @@ #define LIBIDN_REQUIRED_VERSION "0.4.1" -#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ - defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_GSKIT) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) +#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif /* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ +#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_SPNEGO #endif /* Single point where USE_KERBEROS5 definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ +#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_KERBEROS5 #endif /* Single point where USE_NTLM definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM) -# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +#if !defined(CURL_DISABLE_NTLM) +# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_GNUTLS) || defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) # define USE_CURL_NTLM_CORE # endif # if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) diff --git a/vendor/curl/lib/curl_setup_once.h b/vendor/curl/lib/curl_setup_once.h index dde7229d23..c1ed059070 100644 --- a/vendor/curl/lib/curl_setup_once.h +++ b/vendor/curl/lib/curl_setup_once.h @@ -77,6 +77,12 @@ # endif #endif +#ifdef USE_SCHANNEL +/* Must set this before is included directly or indirectly by + another Windows header. */ +# define SCHANNEL_USE_BLACKLISTS 1 +#endif + #ifdef __hpux # if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL) # ifdef _APP32_64BIT_OFF_T diff --git a/vendor/curl/lib/curl_sha256.h b/vendor/curl/lib/curl_sha256.h index c5e157bee1..d99f958f90 100644 --- a/vendor/curl/lib/curl_sha256.h +++ b/vendor/curl/lib/curl_sha256.h @@ -25,7 +25,9 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) + #include #include "curl_hmac.h" diff --git a/vendor/curl/lib/curl_log.c b/vendor/curl/lib/curl_trc.c similarity index 77% rename from vendor/curl/lib/curl_log.c rename to vendor/curl/lib/curl_trc.c index 71024cfc62..76c35ba386 100644 --- a/vendor/curl/lib/curl_log.c +++ b/vendor/curl/lib/curl_trc.c @@ -26,7 +26,7 @@ #include -#include "curl_log.h" +#include "curl_trc.h" #include "urldata.h" #include "easyif.h" #include "cfilters.h" @@ -124,19 +124,17 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...) } } -#ifdef DEBUGBUILD +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { DEBUGASSERT(cf); - if(data && Curl_log_cf_is_debug(cf)) { + if(data && Curl_trc_cf_is_verbose(cf, data)) { va_list ap; int len; char buffer[MAXINFO + 2]; - len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ", - cf->conn->connection_id, cf->sockindex? "/2" : "", - cf->cft->name); + len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name); va_start(ap, fmt); len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); va_end(ap); @@ -181,44 +179,67 @@ static struct Curl_cftype *cf_types[] = { NULL, }; -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -CURLcode Curl_log_init(void) +CURLcode Curl_trc_opt(const char *config) { - const char *setting = getenv("CURL_DEBUG"); - if(setting) { - char *token, *tok_buf, *tmp; - size_t i; - - tmp = strdup(setting); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - token = strtok_r(tmp, ", ", &tok_buf); - while(token) { - for(i = 0; cf_types[i]; ++i) { - if(strcasecompare(token, cf_types[i]->name)) { - cf_types[i]->log_level = CURL_LOG_DEBUG; - break; - } + char *token, *tok_buf, *tmp; + size_t i; + int lvl; + + tmp = strdup(config); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ", ", &tok_buf); + while(token) { + switch(*token) { + case '-': + lvl = CURL_LOG_LVL_NONE; + ++token; + break; + case '+': + lvl = CURL_LOG_LVL_INFO; + ++token; + break; + default: + lvl = CURL_LOG_LVL_INFO; + break; + } + for(i = 0; cf_types[i]; ++i) { + if(strcasecompare(token, "all")) { + cf_types[i]->log_level = lvl; + } + else if(strcasecompare(token, cf_types[i]->name)) { + cf_types[i]->log_level = lvl; + break; } - token = strtok_r(NULL, ", ", &tok_buf); } - free(tmp); + token = strtok_r(NULL, ", ", &tok_buf); + } + free(tmp); + return CURLE_OK; +} + +CURLcode Curl_trc_init(void) +{ +#ifdef DEBUGBUILD + /* WIP: we use the auto-init from an env var only in DEBUG builds for + * convenience. */ + const char *config = getenv("CURL_DEBUG"); + if(config) { + return Curl_trc_opt(config); } +#endif return CURLE_OK; } -#else /* DEBUGBUILD */ +#else /* !CURL_DISABLE_VERBOSE_STRINGS) */ -CURLcode Curl_log_init(void) +CURLcode Curl_trc_init(void) { return CURLE_OK; } #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { (void)data; diff --git a/vendor/curl/lib/curl_trc.h b/vendor/curl/lib/curl_trc.h new file mode 100644 index 0000000000..84b5471d85 --- /dev/null +++ b/vendor/curl/lib/curl_trc.h @@ -0,0 +1,150 @@ +#ifndef HEADER_CURL_TRC_H +#define HEADER_CURL_TRC_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; +struct Curl_cfilter; + +/** + * Init logging, return != 0 on failure. + */ +CURLcode Curl_trc_init(void); + +/** + * Configure tracing. May be called several times during global + * initialization. Later calls may not take effect. + * + * Configuration format supported: + * - comma-separated list of component names to enable logging on. + * E.g. 'http/2,ssl'. Unknown names are ignored. Names are compared + * case-insensitive. + * - component 'all' applies to all known log components + * - prefixing a component with '+' or '-' will en-/disable logging for + * that component + * Example: 'all,-ssl' would enable logging for all components but the + * SSL filters. + * + * @param config configuration string + */ +CURLcode Curl_trc_opt(const char *config); + +/* the function used to output verbose information */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size); + +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +/** + * Output a failure message on registered callbacks for transfer. + */ +void Curl_failf(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +#define failf Curl_failf + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +#else + const char *fmt, ...); +#endif + +#define CURL_LOG_LVL_NONE 0 +#define CURL_LOG_LVL_INFO 1 + + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) +/* informational messages enabled */ + +#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) +#define Curl_trc_cf_is_verbose(cf, data) \ + ((data) && (data)->set.verbose && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) + +/* explainer: we have some mix configuration and werror settings + * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced + * on gnuc and some other compiler. Need to treat carefully. + */ +#if defined(HAVE_VARIADIC_MACROS_C99) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + +#define infof(data, ...) \ + do { if(Curl_trc_is_verbose(data)) \ + Curl_infof(data, __VA_ARGS__); } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { if(Curl_trc_cf_is_verbose(cf, data)) \ + Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) + +#else /* no variadic macro args */ +#define infof Curl_infof +#define CURL_TRC_CF Curl_trc_cf_infof +#endif /* variadic macro args */ + +#else /* !CURL_DISABLE_VERBOSE_STRINGS */ +/* All informational messages are not compiled in for size savings */ + +#define Curl_trc_is_verbose(d) ((void)(d), FALSE) +#define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE) + +#if defined(HAVE_VARIADIC_MACROS_C99) +#define infof(...) Curl_nop_stmt +#define CURL_TRC_CF(...) Curl_nop_stmt +#define Curl_trc_cf_infof(...) Curl_nop_stmt +#elif defined(HAVE_VARIADIC_MACROS_GCC) +#define infof(x...) Curl_nop_stmt +#define CURL_TRC_CF(x...) Curl_nop_stmt +#define Curl_trc_cf_infof(x...) Curl_nop_stmt +#else +#error "missing VARIADIC macro define, fix and rebuild!" +#endif + +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + +#endif /* HEADER_CURL_TRC_H */ diff --git a/vendor/curl/lib/dynbuf.h b/vendor/curl/lib/dynbuf.h index 57ad62b22b..6291eabd37 100644 --- a/vendor/curl/lib/dynbuf.h +++ b/vendor/curl/lib/dynbuf.h @@ -81,8 +81,6 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); #define DYN_PAUSE_BUFFER (64 * 1024 * 1024) #define DYN_HAXPROXY 2048 #define DYN_HTTP_REQUEST (1024*1024) -#define DYN_H2_HEADERS (128*1024) -#define DYN_H2_TRAILERS (128*1024) #define DYN_APRINTF 8000000 #define DYN_RTSP_REQ_HEADER (64*1024) #define DYN_TRAILERS (64*1024) diff --git a/vendor/curl/lib/easy.c b/vendor/curl/lib/easy.c index d36cc03d1f..16bbd35251 100644 --- a/vendor/curl/lib/easy.c +++ b/vendor/curl/lib/easy.c @@ -63,6 +63,7 @@ #include "slist.h" #include "mime.h" #include "amigaos.h" +#include "macos.h" #include "warnless.h" #include "sigpipe.h" #include "vssh/ssh.h" @@ -83,7 +84,7 @@ /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; -static long init_flags; +static long easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE @@ -157,8 +158,8 @@ static CURLcode global_init(long flags, bool memoryfuncs) #endif } - if(Curl_log_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n")); + if(Curl_trc_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n")); goto fail; } @@ -167,19 +168,20 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } -#ifdef WIN32 if(Curl_win32_init(flags)) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); goto fail; } -#endif -#ifdef __AMIGA__ if(Curl_amiga_init()) { DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; } -#endif + + if(Curl_macos_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); + goto fail; + } if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); @@ -199,7 +201,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) } #endif - init_flags = flags; + easy_init_flags = flags; #ifdef DEBUGBUILD if(getenv("CURL_GLOBAL_INIT")) @@ -274,7 +276,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, /** * curl_global_cleanup() globally cleanups curl, uses the value of - * "init_flags" to determine what needs to be cleaned up and what doesn't. + * "easy_init_flags" to determine what needs to be cleaned up and what doesn't. */ void curl_global_cleanup(void) { @@ -294,7 +296,7 @@ void curl_global_cleanup(void) Curl_resolver_global_cleanup(); #ifdef WIN32 - Curl_win32_cleanup(init_flags); + Curl_win32_cleanup(easy_init_flags); #endif Curl_amiga_cleanup(); @@ -308,11 +310,31 @@ void curl_global_cleanup(void) free(leakpointer); #endif - init_flags = 0; + easy_init_flags = 0; global_init_unlock(); } +/** + * curl_global_trace() globally initializes curl logging. + */ +CURLcode curl_global_trace(const char *config) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + CURLcode result; + global_init_lock(); + + result = Curl_trc_opt(config); + + global_init_unlock(); + + return result; +#else + (void)config; + return CURLE_OK; +#endif +} + /* * curl_global_sslset() globally initializes the SSL backend to use. */ @@ -693,7 +715,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi) * * REALITY: it can't just create and destroy the multi handle that easily. It * needs to keep it around since if this easy handle is used again by this - * function, the same multi handle must be re-used so that the same pools and + * function, the same multi handle must be reused so that the same pools and * caches can be used. * * DEBUG: if 'events' is set TRUE, this function will use a replacement engine @@ -893,6 +915,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) /* the connection cache is setup on demand */ outcurl->state.conn_cache = NULL; outcurl->state.lastconnect_id = -1; + outcurl->state.recent_conn_id = -1; + outcurl->id = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; @@ -1040,7 +1064,7 @@ void curl_easy_reset(struct Curl_easy *data) memset(&data->state.authhost, 0, sizeof(struct auth)); memset(&data->state.authproxy, 0, sizeof(struct auth)); -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif } diff --git a/vendor/curl/lib/easy_lock.h b/vendor/curl/lib/easy_lock.h index 5fa9477d06..6399a39bde 100644 --- a/vendor/curl/lib/easy_lock.h +++ b/vendor/curl/lib/easy_lock.h @@ -1,3 +1,5 @@ +#ifndef HEADER_CURL_EASY_LOCK_H +#define HEADER_CURL_EASY_LOCK_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -103,3 +105,5 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock) #undef GLOBAL_INIT_IS_THREADSAFE #endif + +#endif /* HEADER_CURL_EASY_LOCK_H */ diff --git a/vendor/curl/lib/easyoptions.c b/vendor/curl/lib/easyoptions.c index a9c1efd006..e69c658b0c 100644 --- a/vendor/curl/lib/easyoptions.c +++ b/vendor/curl/lib/easyoptions.c @@ -120,6 +120,7 @@ struct curl_easyoption Curl_easyopts[] = { {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOT_LONG, 0}, {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0}, + {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0}, {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0}, {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0}, {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0}, @@ -164,7 +165,9 @@ struct curl_easyoption Curl_easyopts[] = { {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0}, {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0}, {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0}, - {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0}, + {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, + CURLOT_LONG, CURLOT_FLAG_ALIAS}, + {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0}, {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0}, {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, @@ -370,6 +373,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (322 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (323 + 1)); } #endif diff --git a/vendor/curl/lib/fopen.c b/vendor/curl/lib/fopen.c index f710dbf05a..b6e3caddde 100644 --- a/vendor/curl/lib/fopen.c +++ b/vendor/curl/lib/fopen.c @@ -56,13 +56,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, int fd = -1; *tempname = NULL; - if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) { - /* a non-regular file, fallback to direct fopen() */ - *fh = fopen(filename, FOPEN_WRITETEXT); - if(*fh) - return CURLE_OK; + *fh = fopen(filename, FOPEN_WRITETEXT); + if(!*fh) goto fail; - } + if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) + return CURLE_OK; + fclose(*fh); + *fh = NULL; result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix)); if(result) @@ -85,7 +85,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, if((fstat(fd, &nsb) != -1) && (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { /* if the user and group are the same, clone the original mode */ - if(fchmod(fd, sb.st_mode) == -1) + if(fchmod(fd, (mode_t)sb.st_mode) == -1) goto fail; } } diff --git a/vendor/curl/lib/formdata.c b/vendor/curl/lib/formdata.c index 2bdb9f26ec..8984b63223 100644 --- a/vendor/curl/lib/formdata.c +++ b/vendor/curl/lib/formdata.c @@ -27,7 +27,7 @@ #include #include "formdata.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) #include @@ -941,7 +941,7 @@ int curl_formget(struct curl_httppost *form, void *arg, void curl_formfree(struct curl_httppost *form) { (void)form; - /* does nothing HTTP is disabled */ + /* Nothing to do. */ } #endif /* if disabled */ diff --git a/vendor/curl/lib/formdata.h b/vendor/curl/lib/formdata.h index caabb6324c..af466249fd 100644 --- a/vendor/curl/lib/formdata.h +++ b/vendor/curl/lib/formdata.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_MIME +#ifndef CURL_DISABLE_FORM_API /* used by FormAdd for temporary storage */ struct FormInfo { @@ -53,10 +53,7 @@ CURLcode Curl_getformdata(struct Curl_easy *data, curl_mimepart *, struct curl_httppost *post, curl_read_callback fread_func); -#else -/* disabled */ -#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN -#endif +#endif /* CURL_DISABLE_FORM_API */ #endif /* HEADER_CURL_FORMDATA_H */ diff --git a/vendor/curl/lib/ftp.c b/vendor/curl/lib/ftp.c index 402bfb980f..e24ae9c15d 100644 --- a/vendor/curl/lib/ftp.c +++ b/vendor/curl/lib/ftp.c @@ -32,9 +32,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -93,14 +90,14 @@ /* Local API functions */ #ifndef DEBUGBUILD -static void _state(struct Curl_easy *data, - ftpstate newstate); -#define state(x,y) _state(x,y) +static void _ftp_state(struct Curl_easy *data, + ftpstate newstate); +#define ftp_state(x,y) _ftp_state(x,y) #else -static void _state(struct Curl_easy *data, - ftpstate newstate, - int lineno); -#define state(x,y) _state(x,y,__LINE__) +static void _ftp_state(struct Curl_easy *data, + ftpstate newstate, + int lineno); +#define ftp_state(x,y) _ftp_state(x,y,__LINE__) #endif static CURLcode ftp_sendquote(struct Curl_easy *data, @@ -463,7 +460,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) } conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OK; } @@ -591,7 +588,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data, * generically is a good idea. */ infof(data, "We got a 421 - timeout"); - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OPERATION_TIMEDOUT; } @@ -750,10 +747,10 @@ static const char * const ftp_state_names[]={ #endif /* This is the ONLY way to change FTP state! */ -static void _state(struct Curl_easy *data, - ftpstate newstate +static void _ftp_state(struct Curl_easy *data, + ftpstate newstate #ifdef DEBUGBUILD - , int lineno + , int lineno #endif ) { @@ -784,7 +781,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, if(!result) { struct ftp_conn *ftpc = &conn->proto.ftpc; ftpc->ftp_trying_alternative = FALSE; - state(data, FTP_USER); + ftp_state(data, FTP_USER); } return result; } @@ -794,7 +791,7 @@ static CURLcode ftp_state_pwd(struct Curl_easy *data, { CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD"); if(!result) - state(data, FTP_PWD); + ftp_state(data, FTP_PWD); return result; } @@ -865,14 +862,14 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, if(conn->bits.reuse && ftpc->entrypath && /* no need to go to entrypath when we have an absolute path */ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { - /* This is a re-used connection. Since we change directory to where the + /* This is a reused connection. Since we change directory to where the transfer is taking place, we must first get back to the original dir where we ended up after login: */ ftpc->cwdcount = 0; /* we count this as the first path, then we add one for all upcoming ones in the ftp->dirs[] array */ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); if(!result) - state(data, FTP_CWD); + ftp_state(data, FTP_CWD); } else { if(ftpc->dirdepth) { @@ -882,7 +879,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->dirs[ftpc->cwdcount -1]); if(!result) - state(data, FTP_CWD); + ftp_state(data, FTP_CWD); } else { /* No CWD necessary */ @@ -975,7 +972,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(ip_end) { /* either ipv6 or (ipv4|domain|interface):port(-range) */ #ifdef ENABLE_IPV6 - if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) { + if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { /* ipv6 */ port_min = port_max = 0; strcpy(addr, string_ftpport); @@ -1261,11 +1258,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) goto out; portsock = CURL_SOCKET_BAD; /* now held in filter */ - state(data, FTP_PORT); + ftp_state(data, FTP_PORT); out: if(result) { - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); } if(portsock != CURL_SOCKET_BAD) Curl_socket_close(data, conn, portsock); @@ -1307,7 +1304,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); if(!result) { ftpc->count1 = modeoff; - state(data, FTP_PASV); + ftp_state(data, FTP_PASV); infof(data, "Connect data stream passively"); } return result; @@ -1330,7 +1327,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) /* doesn't transfer any data */ /* still possibly do PRE QUOTE jobs */ - state(data, FTP_RETR_PREQUOTE); + ftp_state(data, FTP_RETR_PREQUOTE); result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); } else if(data->set.ftp_use_port) { @@ -1355,7 +1352,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", conn->proto.ftpc.file); if(!result) - state(data, FTP_PRET); + ftp_state(data, FTP_PRET); } else result = ftp_state_use_pasv(data, conn); @@ -1377,7 +1374,7 @@ static CURLcode ftp_state_rest(struct Curl_easy *data, whether it supports range */ result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); if(!result) - state(data, FTP_REST); + ftp_state(data, FTP_REST); } else result = ftp_state_prepare_transfer(data); @@ -1398,7 +1395,7 @@ static CURLcode ftp_state_size(struct Curl_easy *data, /* we know ftpc->file is a valid pointer to a file name */ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - state(data, FTP_SIZE); + ftp_state(data, FTP_SIZE); } else result = ftp_state_rest(data, conn); @@ -1466,7 +1463,7 @@ static CURLcode ftp_state_list(struct Curl_easy *data) free(cmd); if(!result) - state(data, FTP_LIST); + ftp_state(data, FTP_LIST); return result; } @@ -1530,7 +1527,7 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data) result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); if(!result) - state(data, FTP_MDTM); + ftp_state(data, FTP_MDTM); } else result = ftp_state_type(data); @@ -1569,7 +1566,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, /* Got no given size to start from, figure it out */ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - state(data, FTP_STOR_SIZE); + ftp_state(data, FTP_STOR_SIZE); return result; } @@ -1624,7 +1621,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, * ftp_done() because we didn't transfer anything! */ ftp->transfer = PPTRANSFER_NONE; - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OK; } } @@ -1634,7 +1631,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s", ftpc->file); if(!result) - state(data, FTP_STOR); + ftp_state(data, FTP_STOR); return result; } @@ -1695,7 +1692,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); if(result) return result; - state(data, instate); + ftp_state(data, instate); quote = TRUE; } } @@ -1709,7 +1706,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, break; case FTP_RETR_PREQUOTE: if(ftp->transfer != PPTRANSFER_BODY) - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); else { if(ftpc->known_filesize != -1) { Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); @@ -1731,12 +1728,12 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - state(data, FTP_RETR); + ftp_state(data, FTP_RETR); } else { result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) - state(data, FTP_RETR_SIZE); + ftp_state(data, FTP_RETR_SIZE); } } } @@ -1780,7 +1777,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, if(!result) { conn->proto.ftpc.count1++; /* remain in/go to the FTP_PASV state */ - state(data, FTP_PASV); + ftp_state(data, FTP_PASV); } return result; } @@ -1900,7 +1897,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, if(data->set.ftp_skip_ip) { /* 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, re-use %s instead", + infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", ip[0], ip[1], ip[2], ip[3], conn->host.name); ftpc->newhost = strdup(control_address(conn)); @@ -2005,7 +2002,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; conn->bits.do_more = TRUE; - state(data, FTP_STOP); /* this phase is completed */ + ftp_state(data, FTP_STOP); /* this phase is completed */ return result; } @@ -2039,7 +2036,7 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data, } else { infof(data, "Connect data stream actively"); - state(data, FTP_STOP); /* end of DO phase */ + ftp_state(data, FTP_STOP); /* end of DO phase */ result = ftp_dophase_done(data, FALSE); } @@ -2151,7 +2148,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, infof(data, "The requested document is not new enough"); ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ data->info.timecond = TRUE; - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OK; } break; @@ -2160,7 +2157,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, infof(data, "The requested document is not old enough"); ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ data->info.timecond = TRUE; - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OK; } break; @@ -2268,7 +2265,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, /* Set ->transfer so that we won't get any error in ftp_done() * because we didn't transfer the any file */ ftp->transfer = PPTRANSFER_NONE; - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return CURLE_OK; } @@ -2279,13 +2276,13 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T, data->state.resume_from); if(!result) - state(data, FTP_RETR_REST); + ftp_state(data, FTP_RETR_REST); } else { /* no resume */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - state(data, FTP_RETR); + ftp_state(data, FTP_RETR); } return result; @@ -2385,7 +2382,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data, else { result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) - state(data, FTP_RETR); + ftp_state(data, FTP_RETR); } break; } @@ -2401,7 +2398,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data, if(ftpcode >= 400) { failf(data, "Failed FTP upload: %0d", ftpcode); - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); /* oops, we never close the sockets! */ return CURLE_UPLOAD_FAILED; } @@ -2412,7 +2409,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data, if(data->set.ftp_use_port) { bool connected; - state(data, FTP_STOP); /* no longer in STOR state */ + ftp_state(data, FTP_STOP); /* no longer in STOR state */ result = AllowServerConnect(data, &connected); if(result) @@ -2535,7 +2532,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if(!connected) { struct ftp_conn *ftpc = &conn->proto.ftpc; infof(data, "Data conn was not available immediately"); - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); ftpc->wait_data_conn = TRUE; } } @@ -2546,7 +2543,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if((instate == FTP_LIST) && (ftpcode == 450)) { /* simply no matching files in the dir listing */ ftp->transfer = PPTRANSFER_NONE; /* don't download anything */ - state(data, FTP_STOP); /* this phase is over */ + ftp_state(data, FTP_STOP); /* this phase is over */ } else { failf(data, "RETR response: %03d", ftpcode); @@ -2582,7 +2579,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data) */ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0); if(!result) - state(data, FTP_PBSZ); + ftp_state(data, FTP_PBSZ); } else { result = ftp_state_pwd(data, conn); @@ -2605,7 +2602,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", conn->passwd?conn->passwd:""); if(!result) - state(data, FTP_PASS); + ftp_state(data, FTP_PASS); } else if(ftpcode/100 == 2) { /* 230 User ... logged in. @@ -2617,7 +2614,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]); if(!result) - state(data, FTP_ACCT); + ftp_state(data, FTP_ACCT); } else { failf(data, "ACCT requested but none available"); @@ -2638,7 +2635,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); if(!result) { ftpc->ftp_trying_alternative = TRUE; - state(data, FTP_USER); + ftp_state(data, FTP_USER); } } else { @@ -2741,7 +2738,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); if(!result) - state(data, FTP_AUTH); + ftp_state(data, FTP_AUTH); } else result = ftp_state_user(data, conn); @@ -2808,7 +2805,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, Curl_pp_sendf(data, &ftpc->pp, "PROT %c", data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); if(!result) - state(data, FTP_PROT); + ftp_state(data, FTP_PROT); break; case FTP_PROT: @@ -2827,7 +2824,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, */ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); if(!result) - state(data, FTP_CCC); + ftp_state(data, FTP_CCC); } else result = ftp_state_pwd(data, conn); @@ -2919,7 +2916,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, infof(data, "Entry path is '%s'", ftpc->entrypath); /* also save it where getinfo can access it: */ data->state.most_recent_ftp_entrypath = ftpc->entrypath; - state(data, FTP_SYST); + ftp_state(data, FTP_SYST); break; } @@ -2935,7 +2932,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, infof(data, "Failed to figure out path"); } } - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -2970,7 +2967,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* remember target server OS */ Curl_safefree(ftpc->server_os); ftpc->server_os = os; - state(data, FTP_NAMEFMT); + ftp_state(data, FTP_NAMEFMT); break; } /* Nothing special for the target server. */ @@ -2982,7 +2979,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* Cannot identify server OS. Continue anyway and cross fingers. */ } - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -2993,7 +2990,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, break; } - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ + ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ DEBUGF(infof(data, "protocol connect phase DONE")); break; @@ -3026,7 +3023,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s", ftpc->dirs[ftpc->cwdcount - 1]); if(!result) - state(data, FTP_MKD); + ftp_state(data, FTP_MKD); } else { /* return failure */ @@ -3055,7 +3052,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, result = CURLE_REMOTE_ACCESS_DENIED; } else { - state(data, FTP_CWD); + ftp_state(data, FTP_CWD); /* send CWD */ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->dirs[ftpc->cwdcount - 1]); @@ -3114,7 +3111,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); break; } } /* if(ftpcode) */ @@ -3191,7 +3188,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, /* When we connect, we start in the state where we await the 220 response */ - state(data, FTP_WAIT220); + ftp_state(data, FTP_WAIT220); result = ftp_multi_statemach(data, done); @@ -3516,13 +3513,13 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, char want = (char)(ascii?'A':'I'); if(ftpc->transfertype == want) { - state(data, newstate); + ftp_state(data, newstate); return ftp_state_type_resp(data, 200, newstate); } result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); if(!result) { - state(data, newstate); + ftp_state(data, newstate); /* keep track of our current transfer type */ ftpc->transfertype = want; @@ -4039,11 +4036,11 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn) curl_easy_strerror(result)); conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "QUIT command failed"); /* mark for connection closure */ - state(data, FTP_STOP); + ftp_state(data, FTP_STOP); return result; } - state(data, FTP_QUIT); + ftp_state(data, FTP_QUIT); result = ftp_block_statemach(data, conn); } diff --git a/vendor/curl/lib/getinfo.c b/vendor/curl/lib/getinfo.c index 826ffd0b0c..f1574e097b 100644 --- a/vendor/curl/lib/getinfo.c +++ b/vendor/curl/lib/getinfo.c @@ -415,6 +415,13 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, case CURLINFO_RETRY_AFTER: *param_offt = data->info.retry_after; break; + case CURLINFO_XFER_ID: + *param_offt = data->id; + break; + case CURLINFO_CONN_ID: + *param_offt = data->conn? + data->conn->connection_id : data->state.recent_conn_id; + break; default: return CURLE_UNKNOWN_OPTION; } diff --git a/vendor/curl/lib/gopher.c b/vendor/curl/lib/gopher.c index 4a11d9364e..61e41b7e47 100644 --- a/vendor/curl/lib/gopher.c +++ b/vendor/curl/lib/gopher.c @@ -185,7 +185,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(strlen(sel) < 1) break; - result = Curl_write(data, sockfd, sel, k, &amount); + result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); if(result) @@ -227,7 +227,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) free(sel_org); if(!result) - result = Curl_write(data, sockfd, "\r\n", 2, &amount); + result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount); if(result) { failf(data, "Failed sending Gopher request"); return result; diff --git a/vendor/curl/lib/headers.c b/vendor/curl/lib/headers.c index 4367ce797c..3ff4d5eb07 100644 --- a/vendor/curl/lib/headers.c +++ b/vendor/curl/lib/headers.c @@ -300,9 +300,16 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, if(data->state.prevhead) /* line folding, append value to the previous header's value */ return unfold_value(data, header, hlen); - else - /* can't unfold without a previous header */ - return CURLE_BAD_FUNCTION_ARGUMENT; + else { + /* Can't unfold without a previous header. Instead of erroring, just + pass the leading blanks. */ + while(hlen && ISBLANK(*header)) { + header++; + hlen--; + } + if(!hlen) + return CURLE_WEIRD_SERVER_REPLY; + } } hs = calloc(1, sizeof(*hs) + hlen); diff --git a/vendor/curl/lib/hmac.c b/vendor/curl/lib/hmac.c index 8d8de1757d..87e7be8c65 100644 --- a/vendor/curl/lib/hmac.c +++ b/vendor/curl/lib/hmac.c @@ -26,7 +26,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) #include @@ -169,4 +170,4 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, return CURLE_OK; } -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* Using NTLM (without SSPI) or AWS */ diff --git a/vendor/curl/lib/hostip.c b/vendor/curl/lib/hostip.c index d721403b72..acb4b5140b 100644 --- a/vendor/curl/lib/hostip.c +++ b/vendor/curl/lib/hostip.c @@ -67,10 +67,6 @@ #include "curl_memory.h" #include "memdebug.h" -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) -#include -#endif - #if defined(CURLRES_SYNCH) && \ defined(HAVE_ALARM) && \ defined(SIGALRM) && \ @@ -125,19 +121,6 @@ static void freednsentry(void *freethis); -/* - * Return # of addresses in a Curl_addrinfo struct - */ -static int num_addresses(const struct Curl_addrinfo *addr) -{ - int i = 0; - while(addr) { - addr = addr->ai_next; - i++; - } - return i; -} - /* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is @@ -392,6 +375,19 @@ Curl_fetch_addr(struct Curl_easy *data, } #ifndef CURL_DISABLE_SHUFFLE_DNS +/* + * Return # of addresses in a Curl_addrinfo struct + */ +static int num_addresses(const struct Curl_addrinfo *addr) +{ + int i = 0; + while(addr) { + addr = addr->ai_next; + i++; + } + return i; +} + UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, struct Curl_addrinfo **addr); /* @@ -561,6 +557,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) static struct Curl_addrinfo *get_localhost(int port, const char *name) { struct Curl_addrinfo *ca; + struct Curl_addrinfo *ca6; const size_t ss_size = sizeof(struct sockaddr_in); const size_t hostlen = strlen(name); struct sockaddr_in sa; @@ -587,8 +584,12 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) memcpy(ca->ai_addr, &sa, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; strcpy(ca->ai_canonname, name); - ca->ai_next = get_localhost6(port, name); - return ca; + + ca6 = get_localhost6(port, name); + if(!ca6) + return ca; + ca6->ai_next = ca; + return ca6; } #ifdef ENABLE_IPV6 @@ -600,7 +601,7 @@ bool Curl_ipv6works(struct Curl_easy *data) if(data) { /* the nature of most system is that IPv6 status doesn't come and go during a program's lifetime so we only probe the first time and then we - have the info kept for fast re-use */ + have the info kept for fast reuse */ DEBUGASSERT(data); DEBUGASSERT(data->multi); if(data->multi->ipv6_up == IPV6_UNKNOWN) { @@ -743,23 +744,6 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, return CURLRESOLV_ERROR; } -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) - { - /* - * The automagic conversion from IPv4 literals to IPv6 literals only - * works if the SCDynamicStoreCopyProxies system function gets called - * first. As Curl currently doesn't support system-wide HTTP proxies, we - * therefore don't use any value this function might return. - * - * This function is only available on a macOS and is not needed for - * IPv4-only builds, hence the conditions above. - */ - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); - if(dict) - CFRelease(dict); - } -#endif - #ifndef USE_RESOLVE_ON_IPS /* First check if this is an IPv4 address string */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) @@ -1256,7 +1240,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { - infof(data, "RESOLVE %.*s:%d is - old addresses discarded", + infof(data, "RESOLVE %.*s:%d - old addresses discarded", (int)hlen, host_begin, port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. diff --git a/vendor/curl/lib/hsts.c b/vendor/curl/lib/hsts.c index 53c01fc52c..7ecf0042a5 100644 --- a/vendor/curl/lib/hsts.c +++ b/vendor/curl/lib/hsts.c @@ -57,7 +57,7 @@ /* to play well with debug builds, we can *set* a fixed time this will return */ time_t deltatime; /* allow for "adjustments" for unit test purposes */ -static time_t debugtime(void *unused) +static time_t hsts_debugtime(void *unused) { char *timestr = getenv("CURL_TIME"); (void)unused; @@ -70,7 +70,8 @@ static time_t debugtime(void *unused) } return time(NULL); } -#define time(x) debugtime(x) +#undef time +#define time(x) hsts_debugtime(x) #endif struct hsts *Curl_hsts_init(void) diff --git a/vendor/curl/lib/http.c b/vendor/curl/lib/http.c index 219dcc2c00..4344b9dae5 100644 --- a/vendor/curl/lib/http.c +++ b/vendor/curl/lib/http.c @@ -233,7 +233,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data, if(!http) return CURLE_OUT_OF_MEMORY; - Curl_mime_initpart(&http->form); data->req.p.http = http; connkeep(conn, "HTTP default"); @@ -342,6 +341,8 @@ char *Curl_copy_header_value(const char *header) } #ifndef CURL_DISABLE_HTTP_AUTH + +#ifndef CURL_DISABLE_BASIC_AUTH /* * http_output_basic() sets up an Authorization: header (or the proxy version) * for HTTP Basic authentication. @@ -403,6 +404,9 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) return result; } +#endif + +#ifndef CURL_DISABLE_BEARER_AUTH /* * http_output_bearer() sets up an Authorization: header * for HTTP Bearer authentication. @@ -430,6 +434,8 @@ static CURLcode http_output_bearer(struct Curl_easy *data) #endif +#endif + /* pickoneauth() selects the most favourable authentication method from the * ones available and the ones we want. * @@ -446,18 +452,26 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) of preference in case of the existence of multiple accepted types. */ if(avail & CURLAUTH_NEGOTIATE) pick->picked = CURLAUTH_NEGOTIATE; +#ifndef CURL_DISABLE_BEARER_AUTH else if(avail & CURLAUTH_BEARER) pick->picked = CURLAUTH_BEARER; +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH else if(avail & CURLAUTH_DIGEST) pick->picked = CURLAUTH_DIGEST; +#endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; else if(avail & CURLAUTH_NTLM_WB) pick->picked = CURLAUTH_NTLM_WB; +#ifndef CURL_DISABLE_BASIC_AUTH else if(avail & CURLAUTH_BASIC) pick->picked = CURLAUTH_BASIC; +#endif +#ifndef CURL_DISABLE_AWS else if(avail & CURLAUTH_AWS_SIGV4) pick->picked = CURLAUTH_AWS_SIGV4; +#endif else { pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ picked = FALSE; @@ -723,11 +737,11 @@ output_auth_headers(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)conn; -#ifdef CURL_DISABLE_CRYPTO_AUTH +#ifdef CURL_DISABLE_DIGEST_AUTH (void)request; (void)path; #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_AWS if(authstatus->picked == CURLAUTH_AWS_SIGV4) { auth = "AWS_SIGV4"; result = Curl_output_aws_sigv4(data, proxy); @@ -763,7 +777,7 @@ output_auth_headers(struct Curl_easy *data, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(authstatus->picked == CURLAUTH_DIGEST) { auth = "Digest"; result = Curl_output_digest(data, @@ -775,6 +789,7 @@ output_auth_headers(struct Curl_easy *data, } else #endif +#ifndef CURL_DISABLE_BASIC_AUTH if(authstatus->picked == CURLAUTH_BASIC) { /* Basic */ if( @@ -794,6 +809,8 @@ output_auth_headers(struct Curl_easy *data, functions work that way */ authstatus->done = TRUE; } +#endif +#ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ if((!proxy && data->set.str[STRING_BEARER] && @@ -808,6 +825,7 @@ output_auth_headers(struct Curl_easy *data, functions work that way */ authstatus->done = TRUE; } +#endif if(auth) { #ifndef CURL_DISABLE_PROXY @@ -866,7 +884,12 @@ Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || #endif - data->state.aptr.user || data->set.str[STRING_BEARER]) + data->state.aptr.user || +#ifdef USE_SPNEGO + authhost->want & CURLAUTH_NEGOTIATE || + authproxy->want & CURLAUTH_NEGOTIATE || +#endif + data->set.str[STRING_BEARER]) /* continue please */; else { authhost->done = TRUE; @@ -1064,7 +1087,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) { if((authp->avail & CURLAUTH_DIGEST) != 0) infof(data, "Ignoring duplicate digest auth header."); @@ -1087,6 +1110,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } else #endif +#ifndef CURL_DISABLE_BASIC_AUTH if(checkprefix("Basic", auth) && is_valid_auth_separator(auth[5])) { *availp |= CURLAUTH_BASIC; @@ -1101,6 +1125,8 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } } else +#endif +#ifndef CURL_DISABLE_BEARER_AUTH if(checkprefix("Bearer", auth) && is_valid_auth_separator(auth[6])) { *availp |= CURLAUTH_BEARER; @@ -1113,6 +1139,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, data->state.authproblem = TRUE; } } +#endif /* there may be multiple methods on one line, so keep reading */ while(*auth && *auth != ',') /* read up to the next comma */ @@ -1277,7 +1304,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, curl_off_t *bytes_written, /* how much of the buffer contains body data */ curl_off_t included_body_bytes, - int socketindex) + int sockindex) { ssize_t amount; CURLcode result; @@ -1285,12 +1312,9 @@ CURLcode Curl_buffer_send(struct dynbuf *in, size_t size; struct connectdata *conn = data->conn; size_t sendsize; - curl_socket_t sockfd; size_t headersize; - DEBUGASSERT(socketindex <= SECONDARYSOCKET); - - sockfd = Curl_conn_get_socket(data, socketindex); + DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0); /* The looping below is required since we use non-blocking sockets, but due to the circumstances we will just loop and try again and again etc */ @@ -1308,7 +1332,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, || IS_HTTPS_PROXY(conn->http_proxy.proxytype) #endif ) - && conn->httpversion != 20) { + && conn->httpversion < 20) { /* Make sure this doesn't send more body bytes than what the max send speed says. The request bytes do not count to the max speed. */ @@ -1372,9 +1396,25 @@ CURLcode Curl_buffer_send(struct dynbuf *in, else sendsize = size; } + + /* We currently cannot send more that this for http here: + * - if sending blocks, it return 0 as amount + * - we then whisk aside the `in` into the `http` struct + * and install our own `data->state.fread_func` that + * on subsequent calls reads `in` empty. + * - when the whisked away `in` is empty, the `fread_func` + * is restored ot its original state. + * The problem is that `fread_func` can only return + * `upload_buffer_size` lengths. If the send we do here + * is larger and blocks, we do re-sending with smaller + * amounts of data and connection filters do not like + * that. + */ + if(http && (sendsize > (size_t)data->set.upload_buffer_size)) + sendsize = (size_t)data->set.upload_buffer_size; } - result = Curl_write(data, sockfd, ptr, sendsize, &amount); + result = Curl_nwrite(data, sockindex, ptr, sendsize, &amount); if(!result) { /* @@ -1527,7 +1567,7 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "HTTP default"); return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done); @@ -1572,7 +1612,6 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; Curl_dyn_free(&http->send_buffer); - Curl_mime_cleanpart(&http->form); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); Curl_ws_done(data); @@ -2370,45 +2409,53 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, switch(httpreq) { case HTTPREQ_POST_MIME: - http->sendit = &data->set.mimepost; + data->state.mimepost = &data->set.mimepost; break; +#ifndef CURL_DISABLE_FORM_API case HTTPREQ_POST_FORM: - /* Convert the form structure into a mime structure. */ - Curl_mime_cleanpart(&http->form); - result = Curl_getformdata(data, &http->form, data->set.httppost, - data->state.fread_func); - if(result) - return result; - http->sendit = &http->form; + /* Convert the form structure into a mime structure, then keep + the conversion */ + if(!data->state.formp) { + data->state.formp = calloc(sizeof(curl_mimepart), 1); + if(!data->state.formp) + return CURLE_OUT_OF_MEMORY; + Curl_mime_cleanpart(data->state.formp); + result = Curl_getformdata(data, data->state.formp, data->set.httppost, + data->state.fread_func); + if(result) + return result; + data->state.mimepost = data->state.formp; + } break; +#endif default: - http->sendit = NULL; + data->state.mimepost = NULL; } #ifndef CURL_DISABLE_MIME - if(http->sendit) { + if(data->state.mimepost) { const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); /* Read and seek body only. */ - http->sendit->flags |= MIME_BODY_ONLY; + data->state.mimepost->flags |= MIME_BODY_ONLY; /* Prepare the mime structure headers & set content type. */ if(cthdr) for(cthdr += 13; *cthdr == ' '; cthdr++) ; - else if(http->sendit->kind == MIMEKIND_MULTIPART) + else if(data->state.mimepost->kind == MIMEKIND_MULTIPART) cthdr = "multipart/form-data"; - curl_mime_headers(http->sendit, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, http->sendit, cthdr, + curl_mime_headers(data->state.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr, NULL, MIMESTRATEGY_FORM); - curl_mime_headers(http->sendit, NULL, 0); + curl_mime_headers(data->state.mimepost, NULL, 0); if(!result) - result = Curl_mime_rewind(http->sendit); + result = Curl_mime_rewind(data->state.mimepost); if(result) return result; - http->postsize = Curl_mime_size(http->sendit); + http->postsize = Curl_mime_size(data->state.mimepost); } #endif @@ -2564,7 +2611,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, { struct curl_slist *hdr; - for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) { + for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) { result = Curl_dyn_addf(r, "%s\r\n", hdr->data); if(result) return result; @@ -2599,7 +2646,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, /* Read from mime structure. */ data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) http->sendit; + data->state.in = (void *) data->state.mimepost; http->sending = HTTPSEND_BODY; /* this sends the buffer and frees all the buffer resources */ @@ -2667,11 +2714,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #ifndef USE_HYPER /* With Hyper the body is always passed on separately */ if(data->set.postfields) { - - /* In HTTP2, we send request body in DATA frame regardless of - its size. */ - if(conn->httpversion < 20 && - !data->state.expect100header && + if(!data->state.expect100header && (http->postsize < MAX_INITIAL_POST_SIZE)) { /* if we don't use expect: 100 AND postsize is less than MAX_INITIAL_POST_SIZE @@ -2687,7 +2730,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, if(!data->req.upload_chunky) { /* We're not sending it 'chunked', append it to the request - already now to reduce the number if send() calls */ + already now to reduce the number of send() calls */ result = Curl_dyn_addn(r, data->set.postfields, (size_t)http->postsize); included_body = http->postsize; @@ -2832,16 +2875,18 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, } if(co) { struct Cookie *store = co; + size_t clen = 8; /* hold the size of the generated Cookie: header */ /* now loop through all cookies that matched */ while(co) { if(co->value) { - if(0 == count) { + size_t add; + if(!count) { result = Curl_dyn_addn(r, STRCONST("Cookie: ")); if(result) break; } - if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >= - MAX_COOKIE_HEADER_LEN) { + add = strlen(co->name) + strlen(co->value) + 1; + if(clen + add >= MAX_COOKIE_HEADER_LEN) { infof(data, "Restricted outgoing cookies due to header size, " "'%s' not sent", co->name); linecap = TRUE; @@ -2851,6 +2896,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, co->name, co->value); if(result) break; + clen += add + (count ? 2 : 0); count++; } co = co->next; /* next cookie please */ @@ -3025,7 +3071,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, *done = TRUE; return CURLE_OK; } - /* We have a new url to load, but since we want to be able to re-use this + /* We have a new url to load, but since we want to be able to reuse this connection properly, we read the full response in "ignore more" */ k->ignorebody = TRUE; infof(data, "Ignoring the response-body"); @@ -3065,7 +3111,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, data->info.httpcode = 304; infof(data, "Simulate an HTTP 304 response"); /* we abort the transfer before it is completed == we ruin the - re-use ability. Close the connection */ + reuse ability. Close the connection */ streamclose(conn, "Simulated 304 handling"); return CURLE_OK; } @@ -3309,8 +3355,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) altused ? altused : "" ); - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.proxyuserpwd); free(altused); @@ -3381,6 +3427,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } + if(data->req.upload_done) + Curl_conn_ev_data_done_send(data); + if((conn->httpversion >= 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, but is disabled here again to avoid that the chunked encoded version is @@ -3916,6 +3965,29 @@ static CURLcode verify_header(struct Curl_easy *data) return CURLE_OK; } +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only) +{ + size_t bad = 0; + if(delta < MAX_HTTP_RESP_HEADER_SIZE) { + if(!connect_only) + data->req.headerbytecount += (unsigned int)delta; + data->info.header_size += (unsigned int)delta; + if(data->info.header_size > MAX_HTTP_RESP_HEADER_SIZE) + bad = data->info.header_size; + } + else + bad = data->info.header_size + delta; + if(bad) { + failf(data, "Too large response headers: %zu > %u", + bad, MAX_HTTP_RESP_HEADER_SIZE); + return CURLE_RECV_ERROR; + } + return CURLE_OK; +} + + /* * Read any HTTP header lines from the server and pass them to the client app. */ @@ -4054,6 +4126,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* Switching Protocols */ if(k->upgr101 == UPGR101_H2) { /* Switching to HTTP/2 */ + DEBUGASSERT(conn->httpversion < 20); infof(data, "Received 101, Switching to HTTP/2"); k->upgr101 = UPGR101_RECEIVED; @@ -4096,6 +4169,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } else { + if(k->upgr101 == UPGR101_H2) { + /* A requested upgrade was denied, poke the multi handle to possibly + allow a pending pipewait to continue */ + Curl_multi_connchanged(data->multi); + } k->header = FALSE; /* no more header to parse! */ if((k->size == -1) && !k->chunk && !conn->bits.close && @@ -4163,8 +4241,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; - data->info.header_size += (long)headerlen; - data->req.headerbytecount += (long)headerlen; + result = Curl_bump_headersize(data, headerlen, FALSE); + if(result) + return result; /* * When all the headers have been parsed, see if we should give @@ -4227,7 +4306,18 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if((k->httpcode == 417) && data->state.expect100header) { /* 417 Expectation Failed - try again without the Expect header */ - infof(data, "Got 417 while waiting for a 100"); + if(!k->writebytecount && + k->exp100 == EXP100_AWAITING_CONTINUE) { + infof(data, "Got HTTP failure 417 while waiting for a 100"); + } + else { + infof(data, "Got HTTP failure 417 while sending data"); + streamclose(conn, + "Stop sending data before everything sent"); + result = http_perhapsrewind(data, conn); + if(result) + return result; + } data->state.disableexpect = TRUE; DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->state.url); @@ -4486,8 +4576,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; - data->info.header_size += Curl_dyn_len(&data->state.headerb); - data->req.headerbytecount += Curl_dyn_len(&data->state.headerb); + result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb), + FALSE); + if(result) + return result; Curl_dyn_reset(&data->state.headerb); } @@ -4569,8 +4661,8 @@ CURLcode Curl_http_req_make(struct httpreq **preq, if(!req->path) goto out; } - Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS); - Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS); + Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); result = CURLE_OK; out: @@ -4727,8 +4819,8 @@ CURLcode Curl_http_req_make2(struct httpreq **preq, if(result) goto out; - Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS); - Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS); + Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST); result = CURLE_OK; out: @@ -4858,8 +4950,8 @@ CURLcode Curl_http_resp_make(struct http_resp **presp, if(!resp->description) goto out; } - Curl_dynhds_init(&resp->headers, 0, DYN_H2_HEADERS); - Curl_dynhds_init(&resp->trailers, 0, DYN_H2_TRAILERS); + Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST); + Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST); result = CURLE_OK; out: diff --git a/vendor/curl/lib/http.h b/vendor/curl/lib/http.h index df3b4e38b8..9ee3c6537c 100644 --- a/vendor/curl/lib/http.h +++ b/vendor/curl/lib/http.h @@ -64,6 +64,10 @@ extern const struct Curl_handler Curl_handler_wss; struct dynhds; +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only); + /* Header specific functions */ bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ @@ -183,21 +187,19 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); #define EXPECT_100_THRESHOLD (1024*1024) #endif +/* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers + combined that libcurl allows for a single HTTP response, any HTTP + version. This count includes CONNECT response headers. */ +#define MAX_HTTP_RESP_HEADER_SIZE (300*1024) + #endif /* CURL_DISABLE_HTTP */ /**************************************************************************** * HTTP unique setup ***************************************************************************/ struct HTTP { - curl_mimepart *sendit; curl_off_t postsize; /* off_t to handle large file sizes */ const char *postdata; - - const char *p_pragma; /* Pragma: string */ - - /* For FORM posting */ - curl_mimepart form; - struct back { curl_read_callback fread_func; /* backup storage for fread pointer */ void *fread_in; /* backup storage for fread_in pointer */ @@ -292,7 +294,7 @@ void Curl_http_req_free(struct httpreq *req); /** * Create the list of HTTP/2 headers which represent the request, - * using HTTP/2 pseudo headers preceeding the `req->headers`. + * using HTTP/2 pseudo headers preceding the `req->headers`. * * Applies the following transformations: * - if `authority` is set, any "Host" header is removed. diff --git a/vendor/curl/lib/http1.c b/vendor/curl/lib/http1.c index 46fe85509f..1ca7d41e8a 100644 --- a/vendor/curl/lib/http1.c +++ b/vendor/curl/lib/http1.c @@ -38,124 +38,97 @@ #include "memdebug.h" -#define MAX_URL_LEN (4*1024) +#define H1_MAX_URL_LEN (8*1024) void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len) { memset(parser, 0, sizeof(*parser)); parser->max_line_len = max_line_len; - Curl_bufq_init(&parser->scratch, max_line_len, 1); + Curl_dyn_init(&parser->scratch, max_line_len); } void Curl_h1_req_parse_free(struct h1_req_parser *parser) { if(parser) { Curl_http_req_free(parser->req); - Curl_bufq_free(&parser->scratch); + Curl_dyn_free(&parser->scratch); parser->req = NULL; parser->done = FALSE; } } +static CURLcode trim_line(struct h1_req_parser *parser, int options) +{ + DEBUGASSERT(parser->line); + if(parser->line_len) { + if(parser->line[parser->line_len - 1] == '\n') + --parser->line_len; + if(parser->line_len) { + if(parser->line[parser->line_len - 1] == '\r') + --parser->line_len; + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + } + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + } + else if(options & H1_PARSE_OPT_STRICT) + return CURLE_URL_MALFORMAT; + + if(parser->line_len > parser->max_line_len) { + return CURLE_URL_MALFORMAT; + } + return CURLE_OK; +} + static ssize_t detect_line(struct h1_req_parser *parser, - const char *buf, const size_t buflen, int options, + const char *buf, const size_t buflen, CURLcode *err) { const char *line_end; - size_t len; DEBUGASSERT(!parser->line); line_end = memchr(buf, '\n', buflen); if(!line_end) { - *err = (buflen > parser->max_line_len)? CURLE_URL_MALFORMAT : CURLE_AGAIN; + *err = CURLE_AGAIN; return -1; } - len = line_end - buf + 1; - if(len > parser->max_line_len) { - *err = CURLE_URL_MALFORMAT; - return -1; - } - - if(options & H1_PARSE_OPT_STRICT) { - if((len == 1) || (buf[len - 2] != '\r')) { - *err = CURLE_URL_MALFORMAT; - return -1; - } - parser->line = buf; - parser->line_len = len - 2; - } - else { - parser->line = buf; - parser->line_len = len - (((len == 1) || (buf[len - 2] != '\r'))? 1 : 2); - } + parser->line = buf; + parser->line_len = line_end - buf + 1; *err = CURLE_OK; - return (ssize_t)len; + return (ssize_t)parser->line_len; } static ssize_t next_line(struct h1_req_parser *parser, const char *buf, const size_t buflen, int options, CURLcode *err) { - ssize_t nread = 0, n; + ssize_t nread = 0; if(parser->line) { - if(parser->scratch_skip) { - /* last line was from scratch. Remove it now, since we are done - * with it and look for the next one. */ - Curl_bufq_skip_and_shift(&parser->scratch, parser->scratch_skip); - parser->scratch_skip = 0; - } parser->line = NULL; parser->line_len = 0; + Curl_dyn_reset(&parser->scratch); } - if(Curl_bufq_is_empty(&parser->scratch)) { - nread = detect_line(parser, buf, buflen, options, err); - if(nread < 0) { - if(*err != CURLE_AGAIN) + nread = detect_line(parser, buf, buflen, err); + if(nread >= 0) { + if(Curl_dyn_len(&parser->scratch)) { + /* append detected line to scratch to have the complete line */ + *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len); + if(*err) return -1; - /* not a complete line, add to scratch for later revisit */ - nread = Curl_bufq_write(&parser->scratch, - (const unsigned char *)buf, buflen, err); - return nread; + parser->line = Curl_dyn_ptr(&parser->scratch); + parser->line_len = Curl_dyn_len(&parser->scratch); } - /* found one */ + *err = trim_line(parser, options); + if(*err) + return -1; } - else { - const char *sbuf; - size_t sbuflen; - - /* scratch contains bytes from last attempt, add more to it */ - if(buflen) { - const char *line_end; - size_t add_len; - ssize_t pos; - - line_end = memchr(buf, '\n', buflen); - pos = line_end? (line_end - buf + 1) : -1; - add_len = (pos >= 0)? (size_t)pos : buflen; - nread = Curl_bufq_write(&parser->scratch, - (const unsigned char *)buf, add_len, err); - if(nread < 0) { - /* Unable to add anything to scratch is an error, since we should - * have seen a line there then before. */ - if(*err == CURLE_AGAIN) - *err = CURLE_URL_MALFORMAT; - return -1; - } - } - - if(Curl_bufq_peek(&parser->scratch, - (const unsigned char **)&sbuf, &sbuflen)) { - n = detect_line(parser, sbuf, sbuflen, options, err); - if(n < 0 && *err != CURLE_AGAIN) - return -1; /* real error */ - parser->scratch_skip = (size_t)n; - } - else { - /* we SHOULD be able to peek at scratch data */ - DEBUGASSERT(0); - } + else if(*err == CURLE_AGAIN) { + /* no line end in `buf`, add it to our scratch */ + *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen); + nread = (*err)? -1 : (ssize_t)buflen; } return nread; } @@ -190,7 +163,7 @@ static CURLcode start_req(struct h1_req_parser *parser, break; } } - /* no SPACE found or empty TARGET or empy HTTP_VERSION */ + /* no SPACE found or empty TARGET or empty HTTP_VERSION */ if(!target_len || !hv_len) goto out; @@ -231,7 +204,7 @@ static CURLcode start_req(struct h1_req_parser *parser, else { /* origin-form OR absolute-form */ CURLUcode uc; - char tmp[MAX_URL_LEN]; + char tmp[H1_MAX_URL_LEN]; /* default, unless we see an absolute URL */ path = target; @@ -328,7 +301,7 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, goto out; } parser->done = TRUE; - Curl_bufq_free(&parser->scratch); + Curl_dyn_reset(&parser->scratch); /* last chance adjustments */ } else { diff --git a/vendor/curl/lib/http1.h b/vendor/curl/lib/http1.h index 93111efa1b..b1eaa969d9 100644 --- a/vendor/curl/lib/http1.h +++ b/vendor/curl/lib/http1.h @@ -33,11 +33,11 @@ #define H1_PARSE_OPT_NONE (0) #define H1_PARSE_OPT_STRICT (1 << 0) -#define H1_PARSE_DEFAULT_MAX_LINE_LEN (8 * 1024) +#define H1_PARSE_DEFAULT_MAX_LINE_LEN DYN_HTTP_REQUEST struct h1_req_parser { struct httpreq *req; - struct bufq scratch; + struct dynbuf scratch; size_t scratch_skip; const char *line; size_t max_line_len; diff --git a/vendor/curl/lib/http2.c b/vendor/curl/lib/http2.c index 191d8cd335..e0cda76d28 100644 --- a/vendor/curl/lib/http2.c +++ b/vendor/curl/lib/http2.c @@ -41,6 +41,7 @@ #include "urlapi-int.h" #include "cfilters.h" #include "connect.h" +#include "rand.h" #include "strtoofft.h" #include "strdup.h" #include "transfer.h" @@ -69,7 +70,7 @@ #define H2_CHUNK_SIZE (16 * 1024) /* this is how much we want "in flight" for a stream */ #define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) -/* on receving from TLS, we prep for holding a full stream window */ +/* on receiving from TLS, we prep for holding a full stream window */ #define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) /* on send into TLS, we just want to accumulate small frames */ #define H2_NW_SEND_CHUNKS 1 @@ -134,9 +135,11 @@ struct cf_h2_ctx { BIT(conn_closed); BIT(goaway); BIT(enable_push); + BIT(nw_out_blocked); }; /* How to access `call_data` from a cf_h2 filter */ +#undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) \ ((struct cf_h2_ctx *)(cf)->ctx)->call_data @@ -173,8 +176,10 @@ struct stream_ctx { int32_t id; /* HTTP/2 protocol identifier for stream */ struct bufq recvbuf; /* response buffer */ struct bufq sendbuf; /* request buffer */ + struct h1_req_parser h1; /* parsing the request */ struct dynhds resp_trailers; /* response trailer fields */ size_t resp_hds_len; /* amount of response header bytes in recvbuf */ + size_t upload_blocked_len; curl_off_t upload_left; /* number of request bytes left to upload */ char **push_headers; /* allocated array */ @@ -183,6 +188,8 @@ struct stream_ctx { int status_code; /* HTTP response status code */ uint32_t error; /* stream error code */ + uint32_t local_window_size; /* the local recv window size */ + bool resp_hds_complete; /* we have a complete, final response */ bool closed; /* TRUE on stream close */ bool reset; /* TRUE on stream reset */ bool close_handled; /* TRUE if stream closure is handled by libcurl */ @@ -209,9 +216,12 @@ static void drain_stream(struct Curl_cfilter *cf, (void)cf; bits = CURL_CSELECT_IN; - if(!stream->send_closed && stream->upload_left) + if(!stream->send_closed && + (stream->upload_left || stream->upload_blocked_len)) bits |= CURL_CSELECT_OUT; if(data->state.dselect_bits != bits) { + CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x", + stream->id, bits); data->state.dselect_bits = bits; Curl_expire(data, 0, EXPIRE_RUN_NOW); } @@ -245,13 +255,15 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - Curl_dynhds_init(&stream->resp_trailers, 0, DYN_H2_TRAILERS); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); stream->resp_hds_len = 0; stream->bodystarted = FALSE; stream->status_code = -1; stream->closed = FALSE; stream->close_handled = FALSE; stream->error = NGHTTP2_NO_ERROR; + stream->local_window_size = H2_STREAM_WINDOW_SIZE; stream->upload_left = 0; H2_STREAM_LCTX(data) = stream; @@ -273,8 +285,8 @@ static void http2_data_done(struct Curl_cfilter *cf, if(ctx->h2) { if(!stream->closed && stream->id > 0) { /* RST_STREAM */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] premature DATA_DONE, RST stream", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream", + stream->id); if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, stream->id, NGHTTP2_STREAM_CLOSED)) (void)nghttp2_session_send(ctx->h2); @@ -304,6 +316,7 @@ static void http2_data_done(struct Curl_cfilter *cf, Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); Curl_dynhds_free(&stream->resp_trailers); if(stream->push_headers) { /* if they weren't used and then freed before */ @@ -356,8 +369,12 @@ static ssize_t nw_out_writer(void *writer_ctx, { struct Curl_cfilter *cf = writer_ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; - return Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); + nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); + if(nwritten > 0) + CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); + return nwritten; } static ssize_t send_callback(nghttp2_session *h2, @@ -365,6 +382,10 @@ static ssize_t send_callback(nghttp2_session *h2, void *userp); static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +#endif static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *mem, size_t len, void *userp); @@ -380,18 +401,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, static int error_callback(nghttp2_session *session, const char *msg, size_t len, void *userp); -/* - * multi_connchanged() is called to tell that there is a connection in - * this multi handle that has changed state (multiplexing become possible, the - * number of allowed streams changed or similar), and a subsequent use of this - * multi handle should move CONNECT_PEND handles back to CONNECT to have them - * retry. - */ -static void multi_connchanged(struct Curl_multi *multi) -{ - multi->recheckstate = TRUE; -} - /* * Initialize the cfilter context */ @@ -419,6 +428,9 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, nghttp2_session_callbacks_set_send_callback(cbs, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif nghttp2_session_callbacks_set_on_data_chunk_recv_callback( cbs, on_data_chunk_recv); nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); @@ -467,6 +479,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, stream->id); DEBUGASSERT(0); } + CURL_TRC_CF(data, cf, "created session via Upgrade"); } else { nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; @@ -494,6 +507,8 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, /* all set, traffic will be send on connect */ result = CURLE_OK; + CURL_TRC_CF(data, cf, "[0] created h2 session%s", + via_h1_upgrade? " (via h1 upgrade)" : ""); out: if(cbs) @@ -539,8 +554,8 @@ static int h2_process_pending_input(struct Curl_cfilter *cf, break; } else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); } } @@ -580,11 +595,10 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t nread = -1; *input_pending = FALSE; - Curl_attach_connection(data, cf->conn); nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); if(nread != -1) { - DEBUGF(LOG_CF(data, cf, "%zd bytes stray data read before trying " - "h2 connection", nread)); + CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying " + "h2 connection", nread); if(h2_process_pending_input(cf, data, &result) < 0) /* immediate error, considered dead */ alive = FALSE; @@ -592,11 +606,10 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, alive = !should_close_session(ctx); } } - else { + else if(result != CURLE_AGAIN) { /* the read failed so let's say this is dead anyway */ alive = FALSE; } - Curl_detach_connection(data); } return alive; @@ -644,13 +657,16 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf, if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; - DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", - Curl_bufq_len(&ctx->outbufq))); nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); - if(nwritten < 0 && result != CURLE_AGAIN) { + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); + ctx->nw_out_blocked = 1; + } return result; } - return CURLE_OK; + return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; } /* @@ -676,15 +692,17 @@ static ssize_t send_callback(nghttp2_session *h2, nw_out_writer, cf, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { + ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; } failf(data, "Failed sending HTTP2 data"); return NGHTTP2_ERR_CALLBACK_FAILURE; } - if(!nwritten) + if(!nwritten) { + ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; - + } return nwritten; } @@ -839,8 +857,8 @@ static int push_promise(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; int rv; /* one of the CURL_PUSH_* defines */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] PUSH_PROMISE received", - frame->promised_stream_id)); + CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", + frame->promised_stream_id); if(data->multi->push_cb) { struct stream_ctx *stream; struct stream_ctx *newstream; @@ -859,7 +877,7 @@ static int push_promise(struct Curl_cfilter *cf, heads.data = data; heads.frame = frame; /* ask the application */ - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application")); + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application"); stream = H2_STREAM_CTX(data); if(!stream) { @@ -931,7 +949,7 @@ static int push_promise(struct Curl_cfilter *cf, } } else { - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it")); + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); rv = CURL_PUSH_DENY; } fail: @@ -964,23 +982,23 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, struct stream_ctx *stream = H2_STREAM_CTX(data); int32_t stream_id = frame->hd.stream_id; CURLcode result; + size_t rbuflen; int rv; if(!stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No proto pointer", stream_id)); + CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id); return CURLE_FAILED_INIT; } switch(frame->hd.type) { case NGHTTP2_DATA: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[DATA len=%zu pad=%zu], " - "buffered=%zu, window=%d/%d", - stream_id, frame->hd.length, frame->data.padlen, - Curl_bufq_len(&stream->recvbuf), - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id))); + rbuflen = Curl_bufq_len(&stream->recvbuf); + CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d", + stream_id, rbuflen, + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id)); /* If !body started on this stream, then receiving DATA is illegal. */ if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -993,9 +1011,22 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { drain_stream(cf, data, stream); } + else if(rbuflen > stream->local_window_size) { + int32_t wsize = nghttp2_session_get_stream_local_window_size( + ctx->h2, stream->id); + if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) { + /* H2 flow control is not absolute, as the server might not have the + * same view, yet. When we receive more than we want, we enforce + * the local window size again to make nghttp2 send WINDOW_UPATEs + * accordingly. */ + nghttp2_session_set_local_window_size(ctx->h2, + NGHTTP2_FLAG_NONE, + stream->id, + stream->local_window_size); + } + } break; case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[HEADERS]", stream_id)); if(stream->bodystarted) { /* Only valid HEADERS after body started is trailer HEADERS. We buffer them in on_header callback. */ @@ -1018,12 +1049,12 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(result) return result; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] %zu header bytes", - stream_id, Curl_bufq_len(&stream->recvbuf))); + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } drain_stream(cf, data, stream); break; case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[PUSH_PROMISE]", stream_id)); rv = push_promise(cf, data, &frame->push_promise); if(rv) { /* deny! */ DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); @@ -1033,38 +1064,127 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(nghttp2_is_fatal(rv)) return CURLE_SEND_ERROR; else if(rv == CURL_PUSH_ERROROUT) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] fail in PUSH_PROMISE received", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received", + stream_id); return CURLE_RECV_ERROR; } } break; case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[RST]", stream_id)); stream->closed = TRUE; - stream->reset = TRUE; + if(frame->rst_stream.error_code) { + stream->reset = TRUE; + } stream->send_closed = TRUE; data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); break; case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[WINDOW_UPDATE]", stream_id)); if((data->req.keepon & KEEP_SEND_HOLD) && (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after win update", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] un-holding after win update", + stream_id); } break; default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[%x]", - stream_id, frame->hd.type)); break; } return CURLE_OK; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { @@ -1074,26 +1194,52 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, int32_t stream_id = frame->hd.stream_id; DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ DEBUGASSERT(data); switch(frame->hd.type) { case NGHTTP2_SETTINGS: { - uint32_t max_conn = ctx->max_concurrent_streams; - DEBUGF(LOG_CF(data, cf, "FRAME[SETTINGS]")); - ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - ctx->enable_push = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d", - ctx->max_concurrent_streams)); - DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s", - ctx->enable_push ? "TRUE" : "false")); - if(data && max_conn != ctx->max_concurrent_streams) { - /* only signal change if the value actually changed */ - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u", - ctx->max_concurrent_streams)); - multi_connchanged(data->multi); + if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) { + uint32_t max_conn = ctx->max_concurrent_streams; + ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + ctx->enable_push = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; + CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", + ctx->max_concurrent_streams); + CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", + ctx->enable_push ? "TRUE" : "false"); + if(data && max_conn != ctx->max_concurrent_streams) { + /* only signal change if the value actually changed */ + CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u", + ctx->max_concurrent_streams); + Curl_multi_connchanged(data->multi); + } + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if((data->req.keepon & KEEP_SEND_HOLD) && + (data->req.keepon & KEEP_SEND)) { + struct stream_ctx *stream = H2_STREAM_CTX(data); + data->req.keepon &= ~KEEP_SEND_HOLD; + if(stream) { + drain_stream(cf, data, stream); + CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", + stream_id); + } + } } break; } @@ -1102,26 +1248,20 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, ctx->goaway_error = frame->goaway.error_code; ctx->last_stream_id = frame->goaway.last_stream_id; if(data) { - DEBUGF(LOG_CF(data, cf, "FRAME[GOAWAY, error=%d, last_stream=%u]", - ctx->goaway_error, ctx->last_stream_id)); infof(data, "received GOAWAY, error=%d, last_stream=%u", ctx->goaway_error, ctx->last_stream_id); - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); } break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "FRAME[WINDOW_UPDATE]")); - break; default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); + break; } return 0; } data_s = nghttp2_session_get_stream_user_data(session, stream_id); if(!data_s) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No Curl_easy associated", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id); return 0; } @@ -1148,8 +1288,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* Receiving a Stream ID not in the hash should not happen - unless we have aborted a transfer artificially and there were more data in the pipeline. Silently ignore. */ - DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%d] Data for unknown", - stream_id)); + CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", + stream_id); /* consumed explicitly as no one will read it */ nghttp2_session_consume(session, stream_id, len); return 0; @@ -1191,8 +1331,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, return 0; } stream = H2_STREAM_CTX(data_s); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] on_stream_close(), %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1202,6 +1340,11 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->reset = TRUE; data_s->req.keepon &= ~KEEP_SEND_HOLD; + if(stream->error) + CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); + else + CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); drain_stream(cf, data_s, stream); /* remove `data_s` from the nghttp2 stream */ @@ -1211,7 +1354,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream_id); DEBUGASSERT(0); } - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] closed now", stream_id)); return 0; } @@ -1228,8 +1370,6 @@ static int on_begin_headers(nghttp2_session *session, return 0; } - DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called")); - if(frame->hd.type != NGHTTP2_HEADERS) { return 0; } @@ -1335,10 +1475,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] trailer: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); result = Curl_dynhds_add(&stream->resp_trailers, (const char *)name, namelen, (const char *)value, valuelen); @@ -1375,8 +1513,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] status: HTTP/2 %03d", - stream->id, stream->status_code)); + CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", + stream->id, stream->status_code); return 0; } @@ -1399,10 +1537,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] header: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ } @@ -1448,9 +1584,9 @@ static ssize_t req_body_read_callback(nghttp2_session *session, if(nread > 0 && stream->upload_left != -1) stream->upload_left -= nread; - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%zd" - " -> %zd, %d", - stream_id, length, stream->upload_left, nread, result)); + CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%" + CURL_FORMAT_CURL_OFF_T " -> %zd, %d", + stream_id, length, stream->upload_left, nread, result); if(stream->upload_left == 0) *data_flags = NGHTTP2_DATA_FLAG_EOF; @@ -1523,7 +1659,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf, if(!ctx || !ctx->h2 || !stream) goto out; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] data done send", stream->id)); + CURL_TRC_CF(data, cf, "[%d] data done send", stream->id); if(!stream->send_closed) { stream->send_closed = TRUE; if(stream->upload_left) { @@ -1548,18 +1684,13 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, ssize_t rv = 0; if(stream->error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] REFUSED_STREAM, try again on a new " - "connection", stream->id)); + CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " + "connection", stream->id); connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ data->state.refused_stream = TRUE; *err = CURLE_SEND_ERROR; /* trigger Curl_retry_request() later */ return -1; } - else if(stream->reset) { - failf(data, "HTTP/2 stream %u was reset", stream->id); - *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - return -1; - } else if(stream->error != NGHTTP2_NO_ERROR) { failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", stream->id, nghttp2_http2_strerror(stream->error), @@ -1567,6 +1698,11 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, *err = CURLE_HTTP2_STREAM; return -1; } + else if(stream->reset) { + failf(data, "HTTP/2 stream %u was reset", stream->id); + *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + return -1; + } if(!stream->bodystarted) { failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " @@ -1610,7 +1746,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, rv = 0; out: - DEBUGF(LOG_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err)); + CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err); return rv; } @@ -1659,15 +1795,15 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, struct stream_ctx *stream = H2_STREAM_CTX(data); int rv = 0; - if((sweight_wanted(data) != sweight_in_effect(data)) || - (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent) ) { + 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)) ) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; h2_pri_spec(data, &pri_spec); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, stream->id, &pri_spec); @@ -1675,31 +1811,30 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, goto out; } - while(!rv && nghttp2_session_want_write(ctx->h2)) + ctx->nw_out_blocked = 0; + while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) rv = nghttp2_session_send(ctx->h2); out: if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); + CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); return CURLE_SEND_ERROR; } return nw_out_flush(cf, data); } static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + struct stream_ctx *stream, char *buf, size_t len, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); ssize_t nread = -1; *err = CURLE_AGAIN; if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "recvbuf read(len=%zu) -> %zd, %d", - len, nread, *err)); if(nread < 0) goto out; DEBUGASSERT(nread > 0); @@ -1707,13 +1842,13 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0) { if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning CLOSE", stream->id)); + CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); nread = http2_handle_stream_close(cf, data, stream, err); } else if(stream->reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || (ctx->goaway && ctx->last_stream_id < stream->id)) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning ERR", stream->id)); + CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; nread = -1; } @@ -1724,8 +1859,9 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "stream_recv(len=%zu) -> %zd, %d", - len, nread, *err)); + if(nread < 0 && *err != CURLE_AGAIN) + CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", + stream->id, len, nread, *err); return nread; } @@ -1739,8 +1875,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, /* Process network input buffer fist */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); if(h2_process_pending_input(cf, data, &result) < 0) return result; } @@ -1760,8 +1896,6 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, } nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - /* DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); */ if(nread < 0) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data: %d(%s)", result, @@ -1771,9 +1905,14 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, break; } else if(nread == 0) { + CURL_TRC_CF(data, cf, "[0] ingress: connection closed"); ctx->conn_closed = TRUE; break; } + else { + CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", + nread); + } if(h2_process_pending_input(cf, data, &result)) return result; @@ -1795,9 +1934,21 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; struct cf_call_data save; + if(!stream) { + /* Abnormal call sequence: either this transfer has never opened a stream + * (unlikely) or the transfer has been done, cleaned up its resources, but + * a read() is called anyway. It is not clear what the calling sequence + * is for such a case. */ + failf(data, "[%zd-%zd], http/2 recv on a transfer never opened " + "or already cleared", (ssize_t)data->id, + (ssize_t)cf->conn->connection_id); + *err = CURLE_HTTP2; + return -1; + } + CF_DATA_SAVE(save, cf, data); - nread = stream_recv(cf, data, buf, len, err); + nread = stream_recv(cf, data, stream, buf, len, err); if(nread < 0 && *err != CURLE_AGAIN) goto out; @@ -1806,7 +1957,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*err) goto out; - nread = stream_recv(cf, data, buf, len, err); + nread = stream_recv(cf, data, stream, buf, len, err); } if(nread > 0) { @@ -1828,28 +1979,34 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] closed stream, set drain", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); drain_stream(cf, data, stream); } } out: result = h2_progress_egress(cf, data); - if(result) { + if(result == CURLE_AGAIN) { + /* pending data to send, need to be called again. Ideally, we'd + * monitor the socket for POLLOUT, but we might not be in SENDING + * transfer state any longer and are unable to make this happen. + */ + drain_stream(cf, data, stream); + } + else if(result) { *err = result; nread = -1; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_recv(len=%zu) -> %zd %d, " - "buffered=%zu, window=%d/%d, connection %d/%d", - stream->id, len, nread, *err, - Curl_bufq_len(&stream->recvbuf), - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id), - nghttp2_session_get_local_window_size(ctx->h2), - HTTP2_HUGE_WINDOW_SIZE)); + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " + "buffered=%zu, window=%d/%d, connection %d/%d", + stream->id, len, nread, *err, + Curl_bufq_len(&stream->recvbuf), + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id), + nghttp2_session_get_local_window_size(ctx->h2), + HTTP2_HUGE_WINDOW_SIZE); CF_DATA_RESTORE(cf, save); return nread; @@ -1861,16 +2018,15 @@ static ssize_t h2_submit(struct stream_ctx **pstream, { struct cf_h2_ctx *ctx = cf->ctx; struct stream_ctx *stream = NULL; - struct h1_req_parser h1; struct dynhds h2_headers; nghttp2_nv *nva = NULL; - size_t nheader, i; + const void *body = NULL; + size_t nheader, bodylen, i; nghttp2_data_provider data_prd; int32_t stream_id; nghttp2_priority_spec pri_spec; ssize_t nwritten; - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = http2_data_setup(cf, data, &stream); @@ -1879,17 +2035,22 @@ static ssize_t h2_submit(struct stream_ctx **pstream, goto out; } - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(nghttp2_nv) * nheader); @@ -1908,29 +2069,9 @@ static ssize_t h2_submit(struct stream_ctx **pstream, nva[i].flags = NGHTTP2_NV_FLAG_NONE; } -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - - for(i = 0; i < nheader; ++i) { - acc += nva[i].namelen + nva[i].valuelen; - - infof(data, "h2 [%.*s: %.*s]", - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); - } - } - h2_pri_spec(data, &pri_spec); - - DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)", - nghttp2_session_check_request_allowed(ctx->h2), (void *)data)); + if(!nghttp2_session_check_request_allowed(ctx->h2)) + CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); switch(data->state.httpreq) { case HTTPREQ_POST: @@ -1954,27 +2095,69 @@ static ssize_t h2_submit(struct stream_ctx **pstream, NULL, data); } - Curl_safefree(nva); - if(stream_id < 0) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(stream_id), stream_id)); + CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + nghttp2_strerror(stream_id), stream_id); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) submit %s", - stream_id, len, data->state.url)); - infof(data, "Using Stream ID: %u (easy handle %p)", - stream_id, (void *)data); +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + if(Curl_trc_is_verbose(data)) { + size_t acc = 0; + + infof(data, "[HTTP/2] [%d] OPENED stream for %s", + stream_id, data->state.url); + for(i = 0; i < nheader; ++i) { + acc += nva[i].namelen + nva[i].valuelen; + + infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + + if(acc > MAX_ACC) { + infof(data, "[HTTP/2] Warning: The cumulative length of all " + "headers exceeds %d bytes and that could cause the " + "stream to be rejected.", MAX_ACC); + } + } + stream->id = stream_id; + stream->local_window_size = H2_STREAM_WINDOW_SIZE; + if(data->set.max_recv_speed) { + /* We are asked to only receive `max_recv_speed` bytes per second. + * Let's limit our stream window size around that, otherwise the server + * will send in large bursts only. We make the window 50% larger to + * allow for data in flight and avoid stalling. */ + curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1); + n += CURLMAX((n/2), 1); + if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) && + n < (UINT_MAX / H2_CHUNK_SIZE)) { + stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE; + } + } + + body = (const char *)buf + nwritten; + bodylen = len - nwritten; + + if(bodylen) { + /* We have request body to send in DATA frame */ + ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err); + if(n < 0) { + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + nwritten += n; + } out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] submit -> %zd, %d", - stream? stream->id : -1, nwritten, *err)); + CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d", + stream? stream->id : -1, nwritten, *err); + Curl_safefree(nva); *pstream = stream; - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -1982,43 +2165,65 @@ static ssize_t h2_submit(struct stream_ctx **pstream, static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { - /* - * Currently, we send request in this function, but this function is also - * used to send request body. It would be nice to add dedicated function for - * request. - */ struct cf_h2_ctx *ctx = cf->ctx; struct stream_ctx *stream = H2_STREAM_CTX(data); struct cf_call_data save; int rv; ssize_t nwritten; CURLcode result; + int blocked = 0, was_blocked = 0; CF_DATA_SAVE(save, cf, data); if(stream && stream->id != -1) { - if(stream->close_handled) { - infof(data, "stream %u closed", stream->id); - *err = CURLE_HTTP2_STREAM; - nwritten = -1; - goto out; + if(stream->upload_blocked_len) { + /* the data in `buf` has already been submitted or added to the + * buffers, but have been EAGAINed on the last invocation. */ + /* TODO: this assertion triggers in OSSFuzz runs and it is not + * clear why. Disable for now to let OSSFuzz continue its tests. */ + DEBUGASSERT(len >= stream->upload_blocked_len); + if(len < stream->upload_blocked_len) { + /* Did we get called again with a smaller `len`? This should not + * happen. We are not prepared to handle that. */ + failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)", + len, stream->upload_blocked_len); + *err = CURLE_HTTP2; + nwritten = -1; + goto out; + } + nwritten = (ssize_t)stream->upload_blocked_len; + stream->upload_blocked_len = 0; + was_blocked = 1; } else if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a findl + * 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, "[%d] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + nwritten = -1; goto out; } - /* If stream_id != -1, we have dispatched request HEADERS, and now - are going to send or sending request body in DATA frame */ - nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - if(nwritten < 0) { - if(*err != CURLE_AGAIN) + else { + /* If stream_id != -1, we have dispatched request HEADERS and + * optionally request body, and now are going to send or sending + * more request body in DATA frame */ + nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); + if(nwritten < 0 && *err != CURLE_AGAIN) goto out; - nwritten = 0; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] bufq_write(len=%zu) -> %zd, %d", - stream->id, len, nwritten, *err)); if(!Curl_bufq_is_empty(&stream->sendbuf)) { + /* req body data is buffered, resume the potentially suspended stream */ rv = nghttp2_session_resume_data(ctx->h2, stream->id); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; @@ -2026,104 +2231,101 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } } - - result = h2_progress_ingress(cf, data); - if(result) { - *err = result; - nwritten = -1; - goto out; - } - - result = h2_progress_egress(cf, data); - if(result) { - *err = result; - nwritten = -1; - goto out; - } - - if(should_close_session(ctx)) { - if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - } - else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); - *err = CURLE_HTTP2; - nwritten = -1; - } - goto out; - } - - if(!nwritten) { - size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->id); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send: win %u/%zu", - stream->id, - nghttp2_session_get_remote_window_size(ctx->h2), rwin)); - if(rwin == 0) { - /* We cannot upload more as the stream's remote window size - * is 0. We need to receive WIN_UPDATEs before we can continue. - */ - data->req.keepon |= KEEP_SEND_HOLD; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] holding send as remote flow " - "window is exhausted", stream->id)); - } - nwritten = -1; - *err = CURLE_AGAIN; - } - /* handled writing BODY for open stream. */ - goto out; } else { nwritten = h2_submit(&stream, cf, data, buf, len, err); if(nwritten < 0) { goto out; } + DEBUGASSERT(stream); + } - result = h2_progress_ingress(cf, data); - if(result) { - *err = result; - nwritten = -1; - goto out; + /* Call the nghttp2 send loop and flush to write ALL buffered data, + * headers and/or request body completely out to the network */ + result = h2_progress_egress(cf, data); + /* if the stream has been closed in egress handling (nghttp2 does that + * when it does not like the headers, for example */ + if(stream && stream->closed && !was_blocked) { + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + nwritten = -1; + goto out; + } + else if(result == CURLE_AGAIN) { + blocked = 1; + } + else if(result) { + *err = result; + nwritten = -1; + goto out; + } + else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { + /* although we wrote everything that nghttp2 wants to send now, + * there is data left in our stream send buffer unwritten. This may + * be due to the stream's HTTP/2 flow window being exhausted. */ + blocked = 1; + } + + if(stream && blocked && nwritten > 0) { + /* Unable to send all data, due to connection blocked or H2 window + * exhaustion. Data is left in our stream buffer, or nghttp2's internal + * frame buffer or our network out buffer. */ + size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->id); + if(rwin == 0) { + /* H2 flow window exhaustion. We need to HOLD upload until we get + * a WINDOW_UPDATE from the server. */ + data->req.keepon |= KEEP_SEND_HOLD; + CURL_TRC_CF(data, cf, "[%d] holding send as remote flow " + "window is exhausted", stream->id); + } + + /* Whatever the cause, we need to return CURL_EAGAIN for this call. + * We have unwritten state that needs us being invoked again and EAGAIN + * is the only way to ensure that. */ + stream->upload_blocked_len = nwritten; + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " + "blocked_len=%zu", + stream->id, len, + nghttp2_session_get_remote_window_size(ctx->h2), rwin, + nwritten); + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + else if(should_close_session(ctx)) { + /* nghttp2 thinks this session is done. If the stream has not been + * closed, this is an error state for out transfer */ + if(stream->closed) { + nwritten = http2_handle_stream_close(cf, data, stream, err); } - - result = h2_progress_egress(cf, data); - if(result) { - *err = result; + else { + CURL_TRC_CF(data, cf, "send: nothing to do in this session"); + *err = CURLE_HTTP2; nwritten = -1; - goto out; - } - - if(should_close_session(ctx)) { - if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - } - else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); - *err = CURLE_HTTP2; - nwritten = -1; - } - goto out; } } out: if(stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, " - "buffered=%zu, upload_left=%zu, stream-window=%d, " - "connection-window=%d", - stream->id, len, nwritten, *err, - Curl_bufq_len(&stream->sendbuf), - (ssize_t)stream->upload_left, - nghttp2_session_get_stream_remote_window_size( - ctx->h2, stream->id), - nghttp2_session_get_remote_window_size(ctx->h2))); - drain_stream(cf, data, stream); + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " + "h2 windows %d-%d (stream-conn), " + "buffers %zu-%zu (stream-conn)", + stream->id, len, nwritten, *err, + (ssize_t)stream->upload_left, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, stream->id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&stream->sendbuf), + Curl_bufq_len(&ctx->outbufq)); } else { - DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " - "connection-window=%d", - len, nwritten, *err, - nghttp2_session_get_remote_window_size(ctx->h2))); + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " + "connection-window=%d, nw_send_buffer(%zu)", + len, nwritten, *err, + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); return nwritten; @@ -2194,8 +2396,12 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, if(result) goto out; + /* Send out our SETTINGS and ACKs and such. If that blocks, we + * have it buffered and can count this filter as being connected */ result = h2_progress_egress(cf, data); - if(result) + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result) goto out; *done = TRUE; @@ -2203,6 +2409,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); CF_DATA_RESTORE(cf, save); return result; } @@ -2241,8 +2448,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, DEBUGASSERT(data); if(ctx && ctx->h2 && stream) { - uint32_t window = !pause * H2_STREAM_WINDOW_SIZE; - CURLcode result; + uint32_t window = pause? 0 : stream->local_window_size; int rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, @@ -2257,10 +2463,8 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, if(!pause) drain_stream(cf, data, stream); - /* make sure the window update gets sent */ - result = h2_progress_egress(cf, data); - if(result) - return result; + /* attempt to send the window update */ + (void)h2_progress_egress(cf, data); if(!pause) { /* Unpausing a h2 transfer, requires it to be run again. The server @@ -2343,8 +2547,8 @@ static bool cf_h2_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); - DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending)); + CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d", + result, *input_pending); CF_DATA_RESTORE(cf, save); return result; } @@ -2395,7 +2599,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_nghttp2 = { "HTTP/2", CF_TYPE_MULTIPLEX, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, cf_h2_close, @@ -2510,7 +2714,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data, CURLcode result; DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2"))); + DEBUGF(infof(data, "switching to HTTP/2")); result = http2_cfilter_add(&cf, data, conn, sockindex); if(result) @@ -2523,7 +2727,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data, conn->httpversion = 20; /* we know we're on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf->next) { bool done; @@ -2551,7 +2755,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf_h2->next) { bool done; @@ -2569,7 +2773,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, CURLcode result; DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2"))); + DEBUGF(infof(data, "upgrading to HTTP/2")); DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); result = http2_cfilter_add(&cf, data, conn, sockindex); @@ -2608,7 +2812,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, conn->httpversion = 20; /* we know we're on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf->next) { bool done; diff --git a/vendor/curl/lib/http_aws_sigv4.c b/vendor/curl/lib/http_aws_sigv4.c index 806016253f..f39d02cced 100644 --- a/vendor/curl/lib/http_aws_sigv4.c +++ b/vendor/curl/lib/http_aws_sigv4.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) #include "urldata.h" #include "strcase.h" @@ -44,16 +44,16 @@ #include "slist.h" -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - ret = Curl_hmacit(Curl_HMAC_SHA256, \ - (unsigned char *)k, \ - kl, \ - (unsigned char *)d, \ - dl, o); \ - if(ret) { \ - goto fail; \ - } \ +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(Curl_HMAC_SHA256, \ + (unsigned char *)k, \ + kl, \ + (unsigned char *)d, \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ } while(0) #define TIMESTAMP_SIZE 17 @@ -199,10 +199,41 @@ static CURLcode make_headers(struct Curl_easy *data, head = tmp_head; } + /* copy user headers to our header list. the logic is based on how http.c + handles user headers. + + user headers in format 'name:' with no value are used to signal that an + internal header of that name should be removed. those user headers are not + added to this list. + + user headers in format 'name;' with no value are used to signal that a + header of that name with no value should be sent. those user headers are + added to this list but in the format that they will be sent, ie the + semi-colon is changed to a colon for format 'name:'. + + user headers with a value of whitespace only, or without a colon or + semi-colon, are not added to this list. + */ for(l = data->set.headers; l; l = l->next) { - tmp_head = curl_slist_append(head, l->data); - if(!tmp_head) + char *dupdata, *ptr; + char *sep = strchr(l->data, ':'); + if(!sep) + sep = strchr(l->data, ';'); + if(!sep || (*sep == ':' && !*(sep + 1))) + continue; + for(ptr = sep + 1; ISSPACE(*ptr); ++ptr) + ; + if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ + continue; + dupdata = strdup(l->data); + if(!dupdata) goto fail; + dupdata[sep - l->data] = ':'; + tmp_head = Curl_slist_append_nodup(head, dupdata); + if(!tmp_head) { + free(dupdata); + goto fail; + } head = tmp_head; } @@ -214,23 +245,22 @@ static CURLcode make_headers(struct Curl_easy *data, if(!tmp_head) goto fail; head = tmp_head; - *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp); + *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); } else { char *value; - *date_header = strdup(*date_header); - if(!*date_header) - goto fail; - value = strchr(*date_header, ':'); - if(!value) + if(!value) { + *date_header = NULL; goto fail; + } ++value; while(ISBLANK(*value)) ++value; strncpy(timestamp, value, TIMESTAMP_SIZE - 1); timestamp[TIMESTAMP_SIZE - 1] = 0; + *date_header = NULL; } /* alpha-sort in a case sensitive manner */ @@ -370,9 +400,112 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, return ret; } +struct pair { + const char *p; + size_t len; +}; + +static int compare_func(const void *a, const void *b) +{ + const struct pair *aa = a; + const struct pair *bb = b; + return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len); +} + +#define MAX_QUERYPAIRS 64 + +static CURLcode canon_query(struct Curl_easy *data, + const char *query, struct dynbuf *dq) +{ + CURLcode result = CURLE_OK; + int entry = 0; + int i; + const char *p = query; + struct pair array[MAX_QUERYPAIRS]; + struct pair *ap = &array[0]; + if(!query) + return result; + + /* sort the name=value pairs first */ + do { + char *amp; + entry++; + ap->p = p; + amp = strchr(p, '&'); + if(amp) + ap->len = amp - p; /* excluding the ampersand */ + else { + ap->len = strlen(p); + break; + } + ap++; + p = amp + 1; + } while(entry < MAX_QUERYPAIRS); + if(entry == MAX_QUERYPAIRS) { + /* too many query pairs for us */ + failf(data, "aws-sigv4: too many query pairs in URL"); + return CURLE_URL_MALFORMAT; + } + + qsort(&array[0], entry, sizeof(struct pair), compare_func); + + ap = &array[0]; + for(i = 0; !result && (i < entry); i++, ap++) { + size_t len; + const char *q = ap->p; + if(!ap->len) + continue; + for(len = ap->len; len && !result; q++, len--) { + if(ISALNUM(*q)) + result = Curl_dyn_addn(dq, q, 1); + else { + switch(*q) { + case '-': + case '.': + case '_': + case '~': + case '=': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; + case '%': + /* uppercase the following if hexadecimal */ + if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { + char tmp[3]="%"; + tmp[1] = Curl_raw_toupper(q[1]); + tmp[2] = Curl_raw_toupper(q[2]); + result = Curl_dyn_addn(dq, tmp, 3); + q += 2; + len -= 2; + } + else + /* '%' without a following two-digit hex, encode it */ + result = Curl_dyn_addn(dq, "%25", 3); + break; + default: { + /* URL encode */ + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + out[1] = hex[((unsigned char)*q)>>4]; + out[2] = hex[*q & 0xf]; + result = Curl_dyn_addn(dq, out, 3); + break; + } + } + } + } + if(i < entry - 1) { + /* insert ampersands between query pairs */ + result = Curl_dyn_addn(dq, "&", 1); + } + } + return result; +} + + CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) { - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct connectdata *conn = data->conn; size_t len; const char *arg; @@ -388,6 +521,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char date[9]; struct dynbuf canonical_headers; struct dynbuf signed_headers; + struct dynbuf canonical_query; char *date_header = NULL; Curl_HttpReq httpreq; const char *method = NULL; @@ -416,6 +550,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* we init those buffers here, so goto fail will free initialized dynbuf */ Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); /* @@ -431,15 +566,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* provider1[:provider2[:region[:service]]] No string can be longer than N bytes of non-whitespace - */ + */ (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "s", provider0, provider1, region, service); if(!provider0[0]) { - failf(data, "first provider can't be empty"); - ret = CURLE_BAD_FUNCTION_ARGUMENT; + failf(data, "first aws-sigv4 provider can't be empty"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto fail; } else if(!provider1[0]) @@ -448,35 +583,38 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!service[0]) { char *hostdot = strchr(hostname, '.'); if(!hostdot) { - failf(data, "service missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostdot - hostname; if(len > MAX_SIGV4_LEN) { - failf(data, "service too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } strncpy(service, hostname, len); service[len] = '\0'; + infof(data, "aws_sigv4: picked service %s from host", service); + if(!region[0]) { const char *reg = hostdot + 1; const char *hostreg = strchr(reg, '.'); if(!hostreg) { - failf(data, "region missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostreg - reg; if(len > MAX_SIGV4_LEN) { - failf(data, "region too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } strncpy(region, reg, len); region[len] = '\0'; + infof(data, "aws_sigv4: picked region %s from host", region); } } @@ -491,11 +629,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!payload_hash) { if(sign_as_s3) - ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, - sha_hex, content_sha256_hdr); + result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, + sha_hex, content_sha256_hdr); else - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; payload_hash = sha_hex; @@ -514,21 +652,20 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) #else time(&clock); #endif - ret = Curl_gmtime(clock, &tm); - if(ret) { + result = Curl_gmtime(clock, &tm); + if(result) { goto fail; } if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto fail; } - ret = make_headers(data, hostname, timestamp, provider1, - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); - if(ret) + result = make_headers(data, hostname, timestamp, provider1, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers); + if(result) goto fail; - ret = CURLE_OUT_OF_MEMORY; if(*content_sha256_hdr) { /* make_headers() needed this without the \r\n for canonicalization */ @@ -540,6 +677,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) memcpy(date, timestamp, sizeof(date)); date[sizeof(date) - 1] = 0; + result = canon_query(data, data->state.up.query, &canonical_query); + if(result) + goto fail; + result = CURLE_OUT_OF_MEMORY; + canonical_request = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ @@ -549,13 +691,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "%.*s", /* HashedRequestPayload in hex */ method, data->state.up.path, - data->state.up.query ? data->state.up.query : "", + Curl_dyn_ptr(&canonical_query) ? + Curl_dyn_ptr(&canonical_query) : "", Curl_dyn_ptr(&canonical_headers), Curl_dyn_ptr(&signed_headers), (int)payload_hash_len, payload_hash); if(!canonical_request) goto fail; + DEBUGF(infof(data, "Canonical request: %s", canonical_request)); + /* provider 0 lowercase */ Curl_strntolower(provider0, provider0, strlen(provider0)); request_type = curl_maprintf("%s4_request", provider0); @@ -612,14 +757,19 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" - "%s\r\n" + /* + * date_header is added here, only if it wasn't + * user-specified (using CURLOPT_HTTPHEADER). + * date_header includes \r\n + */ + "%s" "%s", /* optional sha256 header includes \r\n */ provider0, user, credential_scope, Curl_dyn_ptr(&signed_headers), sha_hex, - date_header, + date_header ? date_header : "", content_sha256_hdr); if(!auth_headers) { goto fail; @@ -628,9 +778,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) Curl_safefree(data->state.aptr.userpwd); data->state.aptr.userpwd = auth_headers; data->state.authhost.done = TRUE; - ret = CURLE_OK; + result = CURLE_OK; fail: + Curl_dyn_free(&canonical_query); Curl_dyn_free(&canonical_headers); Curl_dyn_free(&signed_headers); free(canonical_request); @@ -639,7 +790,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) free(str_to_sign); free(secret); free(date_header); - return ret; + return result; } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */ diff --git a/vendor/curl/lib/http_digest.c b/vendor/curl/lib/http_digest.c index 8daad99e32..2db3125a8e 100644 --- a/vendor/curl/lib/http_digest.c +++ b/vendor/curl/lib/http_digest.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) #include "urldata.h" #include "strcase.h" diff --git a/vendor/curl/lib/http_digest.h b/vendor/curl/lib/http_digest.h index 7d5cfc1bfd..5f797310fd 100644 --- a/vendor/curl/lib/http_digest.h +++ b/vendor/curl/lib/http_digest.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) /* this is for digest header input */ CURLcode Curl_input_digest(struct Curl_easy *data, @@ -39,6 +39,6 @@ CURLcode Curl_output_digest(struct Curl_easy *data, void Curl_http_auth_cleanup_digest(struct Curl_easy *data); -#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_DIGEST_AUTH */ #endif /* HEADER_CURL_HTTP_DIGEST_H */ diff --git a/vendor/curl/lib/http_proxy.c b/vendor/curl/lib/http_proxy.c index add376ba4f..60bbfbe580 100644 --- a/vendor/curl/lib/http_proxy.c +++ b/vendor/curl/lib/http_proxy.c @@ -69,9 +69,9 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "connect")); + CURL_TRC_CF(data, cf, "connect"); connect_sub: - result = cf->next->cft->connect(cf->next, data, blocking, done); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) return result; @@ -86,7 +86,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, case CURL_HTTP_VERSION_NONE: case CURL_HTTP_VERSION_1_0: case CURL_HTTP_VERSION_1_1: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/1.1")); + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); result = Curl_cf_h1_proxy_insert_after(cf, data); @@ -96,7 +96,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, break; #ifdef USE_NGHTTP2 case CURL_HTTP_VERSION_2: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/2")); + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); infof(data, "CONNECT tunnel: HTTP/2 negotiated"); result = Curl_cf_h2_proxy_insert_after(cf, data); if(result) @@ -105,7 +105,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, break; #endif default: - DEBUGF(LOG_CF(data, cf, "installing subfilter for default HTTP/1.1")); + CURL_TRC_CF(data, cf, "installing subfilter for default HTTP/1.1"); infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); result = CURLE_COULDNT_CONNECT; goto out; @@ -156,7 +156,7 @@ static void http_proxy_cf_destroy(struct Curl_cfilter *cf, struct cf_proxy_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); free(ctx); } @@ -165,7 +165,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf, { struct cf_proxy_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; if(ctx->cf_protocol) { struct Curl_cfilter *f; @@ -181,7 +181,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf, ctx->cf_protocol = NULL; } if(cf->next) - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); } diff --git a/vendor/curl/lib/idn.c b/vendor/curl/lib/idn.c index 5f4b07e018..1f31a9546c 100644 --- a/vendor/curl/lib/idn.c +++ b/vendor/curl/lib/idn.c @@ -68,27 +68,59 @@ WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, #define IDN_MAX_LENGTH 255 -bool Curl_win32_idn_to_ascii(const char *in, char **out) +static CURLcode win32_idn_to_ascii(const char *in, char **out) { - bool success = FALSE; - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + *out = NULL; if(in_w) { wchar_t punycode[IDN_MAX_LENGTH]; - int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); + int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode, + IDN_MAX_LENGTH); curlx_unicodefree(in_w); if(chars) { char *mstr = curlx_convert_wchar_to_UTF8(punycode); if(mstr) { *out = strdup(mstr); curlx_unicodefree(mstr); - if(*out) - success = TRUE; + if(!*out) + return CURLE_OUT_OF_MEMORY; } + else + return CURLE_OUT_OF_MEMORY; } + else + return CURLE_URL_MALFORMAT; } - return success; + return CURLE_OK; +} + +static CURLcode win32_ascii_to_idn(const char *in, char **output) +{ + char *out = NULL; + + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + if(in_w) { + WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */ + int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn, + IDN_MAX_LENGTH); + if(chars) { + /* 'chars' is "the number of characters retrieved" */ + char *mstr = curlx_convert_wchar_to_UTF8(idn); + if(mstr) { + out = strdup(mstr); + curlx_unicodefree(mstr); + if(!out) + return CURLE_OUT_OF_MEMORY; + } + } + else + return CURLE_URL_MALFORMAT; + } + else + return CURLE_URL_MALFORMAT; + *output = out; + return CURLE_OK; } #endif /* USE_WIN32_IDN */ @@ -115,10 +147,15 @@ bool Curl_is_ASCII_name(const char *hostname) /* * Curl_idn_decode() returns an allocated IDN decoded string if it was * possible. NULL on error. + * + * CURLE_URL_MALFORMAT - the host name could not be converted + * CURLE_OUT_OF_MEMORY - memory problem + * */ -static char *idn_decode(const char *input) +static CURLcode idn_decode(const char *input, char **output) { char *decoded = NULL; + CURLcode result = CURLE_OK; #ifdef USE_LIBIDN2 if(idn2_check_version(IDN2_VERSION)) { int flags = IDN2_NFC_INPUT @@ -135,26 +172,68 @@ static char *idn_decode(const char *input) compatibility */ rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL); if(rc != IDN2_OK) - decoded = NULL; + result = CURLE_URL_MALFORMAT; } #elif defined(USE_WIN32_IDN) - if(!Curl_win32_idn_to_ascii(input, &decoded)) - decoded = NULL; + result = win32_idn_to_ascii(input, &decoded); +#endif + if(!result) + *output = decoded; + return result; +} + +static CURLcode idn_encode(const char *puny, char **output) +{ + char *enc = NULL; +#ifdef USE_LIBIDN2 + int rc = idn2_to_unicode_8z8z(puny, &enc, 0); + if(rc != IDNA_SUCCESS) + return rc == IDNA_MALLOC_ERROR ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT; +#elif defined(USE_WIN32_IDN) + CURLcode result = win32_ascii_to_idn(puny, &enc); + if(result) + return result; #endif - return decoded; + *output = enc; + return CURLE_OK; } -char *Curl_idn_decode(const char *input) +CURLcode Curl_idn_decode(const char *input, char **output) { - char *d = idn_decode(input); + char *d = NULL; + CURLcode result = idn_decode(input, &d); #ifdef USE_LIBIDN2 - if(d) { + if(!result) { char *c = strdup(d); idn2_free(d); - d = c; + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; } #endif - return d; + if(!result) + *output = d; + return result; +} + +CURLcode Curl_idn_encode(const char *puny, char **output) +{ + char *d = NULL; + CURLcode result = idn_encode(puny, &d); +#ifdef USE_LIBIDN2 + if(!result) { + char *c = strdup(d); + idn2_free(d); + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; + } +#endif + if(!result) + *output = d; + return result; } /* @@ -182,8 +261,9 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) #ifdef USE_IDN /* Check name for non-ASCII and convert hostname if we can */ if(!Curl_is_ASCII_name(host->name)) { - char *decoded = idn_decode(host->name); - if(decoded) { + char *decoded; + CURLcode result = idn_decode(host->name, &decoded); + if(!result) { if(!*decoded) { /* zero length is a bad host name */ Curl_idn_free(decoded); @@ -195,7 +275,7 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) host->name = host->encalloc; } else - return CURLE_URL_MALFORMAT; + return result; } #endif return CURLE_OK; diff --git a/vendor/curl/lib/idn.h b/vendor/curl/lib/idn.h index 6c0bbb7109..74bbcaf498 100644 --- a/vendor/curl/lib/idn.h +++ b/vendor/curl/lib/idn.h @@ -24,15 +24,13 @@ * ***************************************************************************/ -#ifdef USE_WIN32_IDN -bool Curl_win32_idn_to_ascii(const char *in, char **out); -#endif /* USE_WIN32_IDN */ bool Curl_is_ASCII_name(const char *hostname); CURLcode Curl_idnconvert_hostname(struct hostname *host); #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); -char *Curl_idn_decode(const char *input); +CURLcode Curl_idn_decode(const char *input, char **output); +CURLcode Curl_idn_encode(const char *input, char **output); #ifdef USE_LIBIDN2 #define Curl_idn_free(x) idn2_free(x) #else diff --git a/vendor/curl/lib/if2ip.c b/vendor/curl/lib/if2ip.c index 6bf0ce16f7..5249f6cc7e 100644 --- a/vendor/curl/lib/if2ip.c +++ b/vendor/curl/lib/if2ip.c @@ -92,6 +92,8 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) } #endif +#ifndef CURL_DISABLE_BINDLOCAL + #if defined(HAVE_GETIFADDRS) if2ip_result_t Curl_if2ip(int af, @@ -254,3 +256,5 @@ if2ip_result_t Curl_if2ip(int af, } #endif + +#endif /* CURL_DISABLE_BINDLOCAL */ diff --git a/vendor/curl/lib/imap.c b/vendor/curl/lib/imap.c index ed197c9316..de64c2a7f2 100644 --- a/vendor/curl/lib/imap.c +++ b/vendor/curl/lib/imap.c @@ -45,9 +45,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -385,11 +382,11 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * state() + * imap_state() * * This is the ONLY way to change IMAP state! */ -static void state(struct Curl_easy *data, imapstate newstate) +static void imap_state(struct Curl_easy *data, imapstate newstate) { struct imap_conn *imapc = &data->conn->proto.imapc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -441,7 +438,7 @@ static CURLcode imap_perform_capability(struct Curl_easy *data, result = imap_sendf(data, "CAPABILITY"); if(!result) - state(data, IMAP_CAPABILITY); + imap_state(data, IMAP_CAPABILITY); return result; } @@ -458,7 +455,7 @@ static CURLcode imap_perform_starttls(struct Curl_easy *data) CURLcode result = imap_sendf(data, "STARTTLS"); if(!result) - state(data, IMAP_STARTTLS); + imap_state(data, IMAP_STARTTLS); return result; } @@ -487,7 +484,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, if(!result) { imapc->ssldone = ssldone; if(imapc->state != IMAP_UPGRADETLS) - state(data, IMAP_UPGRADETLS); + imap_state(data, IMAP_UPGRADETLS); if(imapc->ssldone) { imap_to_imaps(conn); @@ -514,7 +511,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 don't */ if(!data->state.aptr.user) { - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -531,7 +528,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, free(passwd); if(!result) - state(data, IMAP_LOGIN); + imap_state(data, IMAP_LOGIN); return result; } @@ -615,7 +612,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, with and end the connect phase if we don't */ if(imapc->preauth || !Curl_sasl_can_authenticate(&imapc->sasl, data)) { - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -624,7 +621,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, if(!result) { if(progress == SASL_INPROGRESS) - state(data, IMAP_AUTHENTICATE); + imap_state(data, IMAP_AUTHENTICATE); else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) /* Perform clear text authentication */ result = imap_perform_login(data, conn); @@ -667,7 +664,7 @@ static CURLcode imap_perform_list(struct Curl_easy *data) } if(!result) - state(data, IMAP_LIST); + imap_state(data, IMAP_LIST); return result; } @@ -707,7 +704,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data) free(mailbox); if(!result) - state(data, IMAP_SELECT); + imap_state(data, IMAP_SELECT); return result; } @@ -749,7 +746,7 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data) return CURLE_URL_MALFORMAT; } if(!result) - state(data, IMAP_FETCH); + imap_state(data, IMAP_FETCH); return result; } @@ -820,7 +817,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) free(mailbox); if(!result) - state(data, IMAP_APPEND); + imap_state(data, IMAP_APPEND); return result; } @@ -846,7 +843,7 @@ static CURLcode imap_perform_search(struct Curl_easy *data) result = imap_sendf(data, "SEARCH %s", imap->query); if(!result) - state(data, IMAP_SEARCH); + imap_state(data, IMAP_SEARCH); return result; } @@ -863,7 +860,7 @@ static CURLcode imap_perform_logout(struct Curl_easy *data) CURLcode result = imap_sendf(data, "LOGOUT"); if(!result) - state(data, IMAP_LOGOUT); + imap_state(data, IMAP_LOGOUT); return result; } @@ -1017,7 +1014,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - state(data, IMAP_STOP); /* Authenticated */ + imap_state(data, IMAP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) @@ -1049,7 +1046,7 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data, } else /* End of connect phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -1075,7 +1072,7 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, result = CURLE_QUOTE_ERROR; else /* End of DO phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -1094,10 +1091,19 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, if(imapcode == '*') { /* See if this is an UIDVALIDITY response */ - char tmp[20]; - if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { - Curl_safefree(imapc->mailbox_uidvalidity); - imapc->mailbox_uidvalidity = strdup(tmp); + if(checkprefix("OK [UIDVALIDITY ", line + 2)) { + size_t len = 0; + const char *p = &line[2] + strlen("OK [UIDVALIDITY "); + while((len < 20) && p[len] && ISDIGIT(p[len])) + len++; + if(len && (p[len] == ']')) { + struct dynbuf uid; + Curl_dyn_init(&uid, 20); + if(Curl_dyn_addn(&uid, p, len)) + return CURLE_OUT_OF_MEMORY; + Curl_safefree(imapc->mailbox_uidvalidity); + imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid); + } } } else if(imapcode == IMAP_RESP_OK) { @@ -1109,7 +1115,10 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, } else { /* Note the currently opened mailbox on this connection */ + DEBUGASSERT(!imapc->mailbox); imapc->mailbox = strdup(imap->mailbox); + if(!imapc->mailbox) + return CURLE_OUT_OF_MEMORY; if(imap->custom) result = imap_perform_list(data); @@ -1143,7 +1152,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(imapcode != '*') { Curl_pgrsSetDownloadSize(data, -1); - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return CURLE_REMOTE_FILE_NOT_FOUND; } @@ -1178,7 +1187,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(!chunk) { /* no size, we're done with the data */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return CURLE_OK; } result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk); @@ -1224,7 +1233,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, } /* End of DO phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -1242,7 +1251,7 @@ static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; else /* End of DONE phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -1265,7 +1274,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode, Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); } return result; @@ -1284,7 +1293,7 @@ static CURLcode imap_state_append_final_resp(struct Curl_easy *data, result = CURLE_UPLOAD_FAILED; else /* End of DONE phase */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); return result; } @@ -1372,7 +1381,7 @@ static CURLcode imap_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - state(data, IMAP_STOP); + imap_state(data, IMAP_STOP); break; } } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); @@ -1475,7 +1484,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - state(data, IMAP_SERVERGREET); + imap_state(data, IMAP_SERVERGREET); /* Start off with an response id of '*' */ strcpy(imapc->resptag, "*"); @@ -1516,12 +1525,12 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status, /* Handle responses after FETCH or APPEND transfer has finished */ if(!data->state.upload && data->set.mimepost.kind == MIMEKIND_NONE) - state(data, IMAP_FETCH_FINAL); + imap_state(data, IMAP_FETCH_FINAL); else { /* End the APPEND command first by sending an empty line */ result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", ""); if(!result) - state(data, IMAP_APPEND_FINAL); + imap_state(data, IMAP_APPEND_FINAL); } /* Run the state-machine */ @@ -1777,7 +1786,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) /* Calculate the tag based on the connection ID and command ID */ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi(data->conn->connection_id % 26), + 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)), ++imapc->cmdid); /* start with a blank buffer */ @@ -1806,79 +1815,37 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) */ static char *imap_atom(const char *str, bool escape_only) { - /* !checksrc! disable PARENBRACE 1 */ - const char atom_specials[] = "(){ %*]"; - const char *p1; - char *p2; - size_t backsp_count = 0; - size_t quote_count = 0; - bool others_exists = FALSE; - size_t newlen = 0; - char *newstr = NULL; + struct dynbuf line; + size_t nclean; + size_t len; if(!str) return NULL; - /* Look for "atom-specials", counting the backslash and quote characters as - these will need escaping */ - p1 = str; - while(*p1) { - if(*p1 == '\\') - backsp_count++; - else if(*p1 == '"') - quote_count++; - else if(!escape_only) { - const char *p3 = atom_specials; - - while(*p3 && !others_exists) { - if(*p1 == *p3) - others_exists = TRUE; - - p3++; - } - } - - p1++; - } - - /* Does the input contain any "atom-special" characters? */ - if(!backsp_count && !quote_count && !others_exists) + len = strlen(str); + nclean = strcspn(str, "() {%*]\\\""); + if(len == nclean) + /* nothing to escape, return a strdup */ return strdup(str); - /* Calculate the new string length */ - newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2); + Curl_dyn_init(&line, 2000); - /* Allocate the new string */ - newstr = (char *) malloc((newlen + 1) * sizeof(char)); - if(!newstr) + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) return NULL; - /* Surround the string in quotes if necessary */ - p2 = newstr; - if(!escape_only) { - newstr[0] = '"'; - newstr[newlen - 1] = '"'; - p2++; - } - - /* Copy the string, escaping backslash and quote characters along the way */ - p1 = str; - while(*p1) { - if(*p1 == '\\' || *p1 == '"') { - *p2 = '\\'; - p2++; - } - - *p2 = *p1; - - p1++; - p2++; + while(*str) { + if((*str == '\\' || *str == '"') && + Curl_dyn_addn(&line, "\\", 1)) + return NULL; + if(Curl_dyn_addn(&line, str, 1)) + return NULL; + str++; } - /* Terminate the string */ - newstr[newlen] = '\0'; + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) + return NULL; - return newstr; + return Curl_dyn_ptr(&line); } /*********************************************************************** @@ -1925,6 +1892,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; const char *ptr = conn->options; + bool prefer_login = false; while(!result && ptr && *ptr) { const char *key = ptr; @@ -1938,26 +1906,39 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) while(*ptr && *ptr != ';') ptr++; - if(strncasecompare(key, "AUTH=", 5)) + if(strncasecompare(key, "AUTH=+LOGIN", 11)) { + /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ + prefer_login = true; + imapc->sasl.prefmech = SASL_AUTH_NONE; + } + else if(strncasecompare(key, "AUTH=", 5)) { + prefer_login = false; result = Curl_sasl_parse_url_auth_option(&imapc->sasl, value, ptr - value); - else + } + else { + prefer_login = false; result = CURLE_URL_MALFORMAT; + } if(*ptr == ';') ptr++; } - switch(imapc->sasl.prefmech) { - case SASL_AUTH_NONE: - imapc->preftype = IMAP_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - imapc->preftype = IMAP_TYPE_ANY; - break; - default: - imapc->preftype = IMAP_TYPE_SASL; - break; + if(prefer_login) + imapc->preftype = IMAP_TYPE_CLEARTEXT; + else { + switch(imapc->sasl.prefmech) { + case SASL_AUTH_NONE: + imapc->preftype = IMAP_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + imapc->preftype = IMAP_TYPE_ANY; + break; + default: + imapc->preftype = IMAP_TYPE_SASL; + break; + } } return result; diff --git a/vendor/curl/lib/inet_ntop.c b/vendor/curl/lib/inet_ntop.c index fa9077376b..135f4866c6 100644 --- a/vendor/curl/lib/inet_ntop.c +++ b/vendor/curl/lib/inet_ntop.c @@ -117,8 +117,9 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if(words[i] == 0) { - if(cur.base == -1) - cur.base = i, cur.len = 1; + if(cur.base == -1) { + cur.base = i; cur.len = 1; + } else cur.len++; } diff --git a/vendor/curl/lib/krb5.c b/vendor/curl/lib/krb5.c index 5484447688..18e73debbf 100644 --- a/vendor/curl/lib/krb5.c +++ b/vendor/curl/lib/krb5.c @@ -72,7 +72,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, char *sptr = s; CURLcode result = CURLE_OK; #ifdef HAVE_GSSAPI - enum protection_level data_sec = conn->data_prot; + unsigned char data_sec = conn->data_prot; #endif if(!cmd) @@ -91,7 +91,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_write(data, conn->sock[FIRSTSOCKET], sptr, write_len, + result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len, &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); @@ -261,7 +261,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) } /* We pass NULL as |output_name_type| to avoid a leak. */ gss_display_name(&min, gssname, &output_buffer, NULL); - infof(data, "Trying against %s", output_buffer.value); + infof(data, "Trying against %s", (char *)output_buffer.value); gssresp = GSS_C_NO_BUFFER; *context = GSS_C_NO_CONTEXT; @@ -385,7 +385,7 @@ static const struct Curl_sec_client_mech Curl_krb5_client_mech = { }; static const struct { - enum protection_level level; + unsigned char level; const char *name; } level_names[] = { { PROT_CLEAR, "clear" }, @@ -394,8 +394,7 @@ static const struct { { PROT_PRIVATE, "private" } }; -static enum protection_level -name_to_level(const char *name) +static unsigned char name_to_level(const char *name) { int i; for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) @@ -734,7 +733,7 @@ static int sec_set_protection_level(struct Curl_easy *data) { int code; struct connectdata *conn = data->conn; - enum protection_level level = conn->request_data_prot; + unsigned char level = conn->request_data_prot; DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); @@ -793,7 +792,7 @@ static int sec_set_protection_level(struct Curl_easy *data) int Curl_sec_request_prot(struct connectdata *conn, const char *level) { - enum protection_level l = name_to_level(level); + unsigned char l = name_to_level(level); if(l == PROT_NONE) return -1; DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); diff --git a/vendor/curl/lib/ldap.c b/vendor/curl/lib/ldap.c index 4c88b0aaeb..33a4dea0a8 100644 --- a/vendor/curl/lib/ldap.c +++ b/vendor/curl/lib/ldap.c @@ -50,6 +50,14 @@ #endif #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4201) +# endif +# include /* for [P]UNICODE_STRING */ +# ifdef _MSC_VER +# pragma warning(pop) +# endif # include # ifndef LDAP_VENDOR_NAME # error Your Platform SDK is NOT sufficient for LDAP support! \ @@ -231,7 +239,7 @@ static int ldap_win_bind_auth(LDAP *server, const char *user, } else #endif -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_DIGEST_AUTH) if(authflags & CURLAUTH_DIGEST) { method = LDAP_AUTH_DIGEST; } @@ -754,7 +762,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* no data to transfer */ Curl_setup_transfer(data, -1, -1, FALSE, -1); - connclose(conn, "LDAP connection always disable re-use"); + connclose(conn, "LDAP connection always disable reuse"); return result; } diff --git a/vendor/curl/lib/vtls/nssg.h b/vendor/curl/lib/macos.c similarity index 57% rename from vendor/curl/lib/vtls/nssg.h rename to vendor/curl/lib/macos.c index ad7eef5801..9e8e76e867 100644 --- a/vendor/curl/lib/vtls/nssg.h +++ b/vendor/curl/lib/macos.c @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_NSSG_H -#define HEADER_CURL_NSSG_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,19 +21,35 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + #include "curl_setup.h" -#ifdef USE_NSS -/* - * This header should only be needed to get included by vtls.c and nss.c - */ +#ifdef CURL_MACOS_CALL_COPYPROXIES + +#include -#include "urldata.h" +#include "macos.h" -/* initialize NSS library if not already */ -CURLcode Curl_nss_force_init(struct Curl_easy *data); +#include -extern const struct Curl_ssl Curl_ssl_nss; +CURLcode Curl_macos_init(void) +{ + { + /* + * The automagic conversion from IPv4 literals to IPv6 literals only + * works if the SCDynamicStoreCopyProxies system function gets called + * first. As Curl currently doesn't support system-wide HTTP proxies, we + * therefore don't use any value this function might return. + * + * This function is only available on macOS and is not needed for + * IPv4-only builds, hence the conditions for defining + * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h. + */ + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if(dict) + CFRelease(dict); + } + return CURLE_OK; +} -#endif /* USE_NSS */ -#endif /* HEADER_CURL_NSSG_H */ +#endif diff --git a/vendor/curl/lib/vtls/gskit.h b/vendor/curl/lib/macos.h similarity index 79% rename from vendor/curl/lib/vtls/gskit.h rename to vendor/curl/lib/macos.h index c71e6a0117..637860e80f 100644 --- a/vendor/curl/lib/vtls/gskit.h +++ b/vendor/curl/lib/macos.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_GSKIT_H -#define HEADER_CURL_GSKIT_H +#ifndef HEADER_CURL_MACOS_H +#define HEADER_CURL_MACOS_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,18 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + #include "curl_setup.h" -/* - * This header should only be needed to get included by vtls.c and gskit.c - */ +#ifdef CURL_MACOS_CALL_COPYPROXIES -#include "urldata.h" +CURLcode Curl_macos_init(void); -#ifdef USE_GSKIT +#else -extern const struct Curl_ssl Curl_ssl_gskit; +#define Curl_macos_init() CURLE_OK -#endif /* USE_GSKIT */ +#endif -#endif /* HEADER_CURL_GSKIT_H */ +#endif /* HEADER_CURL_MACOS_H */ diff --git a/vendor/curl/lib/md4.c b/vendor/curl/lib/md4.c index 9ff093b1ca..30ab62e602 100644 --- a/vendor/curl/lib/md4.c +++ b/vendor/curl/lib/md4.c @@ -42,6 +42,7 @@ #ifdef USE_WOLFSSL #include +#define VOID_MD4_INIT #ifdef NO_MD4 #define WOLFSSL_NO_MD4 #endif @@ -92,9 +93,10 @@ typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { md4_init(ctx); + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -114,9 +116,9 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #elif defined(AN_APPLE_OS) typedef CC_MD4_CTX MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { - (void)CC_MD4_Init(ctx); + return CC_MD4_Init(ctx); } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -137,15 +139,22 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->hCryptProv = 0; ctx->hHash = 0; - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash); + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return 0; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return 0; } + + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -176,10 +185,11 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->data = NULL; ctx->size = 0; + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -258,7 +268,7 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx); +static int MD4_Init(MD4_CTX *ctx); static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); static void MD4_Final(unsigned char *result, MD4_CTX *ctx); @@ -397,7 +407,7 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size) return ptr; } -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -406,6 +416,7 @@ static void MD4_Init(MD4_CTX *ctx) ctx->lo = 0; ctx->hi = 0; + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -496,14 +507,21 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #endif /* CRYPTO LIBS */ -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len) +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len) { MD4_CTX ctx; +#ifdef VOID_MD4_INIT MD4_Init(&ctx); +#else + if(!MD4_Init(&ctx)) + return CURLE_FAILED_INIT; +#endif + MD4_Update(&ctx, input, curlx_uztoui(len)); MD4_Final(output, &ctx); + return CURLE_OK; } #endif /* USE_CURL_NTLM_CORE */ diff --git a/vendor/curl/lib/md5.c b/vendor/curl/lib/md5.c index 0a02cc02ac..01415af911 100644 --- a/vendor/curl/lib/md5.c +++ b/vendor/curl/lib/md5.c @@ -24,7 +24,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) #include #include @@ -213,7 +214,8 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) { CryptReleaseContext(ctx->hCryptProv, 0); - return CURLE_OUT_OF_MEMORY; + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; } return CURLE_OK; @@ -651,4 +653,4 @@ CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) return CURLE_OK; } -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* Using NTLM (without SSPI) || Digest */ diff --git a/vendor/curl/lib/mime.c b/vendor/curl/lib/mime.c index 39aac8f241..842b2da7e6 100644 --- a/vendor/curl/lib/mime.c +++ b/vendor/curl/lib/mime.c @@ -84,7 +84,7 @@ static const struct mime_encoder encoders[] = { }; /* Base64 encoding table */ -static const char base64[] = +static const char base64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* Quoted-printable character class table. @@ -469,10 +469,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = st->buf[st->bufbeg++] & 0xFF; i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); - *ptr++ = base64[(i >> 18) & 0x3F]; - *ptr++ = base64[(i >> 12) & 0x3F]; - *ptr++ = base64[(i >> 6) & 0x3F]; - *ptr++ = base64[i & 0x3F]; + *ptr++ = base64enc[(i >> 18) & 0x3F]; + *ptr++ = base64enc[(i >> 12) & 0x3F]; + *ptr++ = base64enc[(i >> 6) & 0x3F]; + *ptr++ = base64enc[i & 0x3F]; cursize += 4; st->pos += 4; size -= 4; @@ -496,10 +496,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; i |= (st->buf[st->bufbeg] & 0xFF) << 16; - ptr[0] = base64[(i >> 18) & 0x3F]; - ptr[1] = base64[(i >> 12) & 0x3F]; + ptr[0] = base64enc[(i >> 18) & 0x3F]; + ptr[1] = base64enc[(i >> 12) & 0x3F]; if(++st->bufbeg != st->bufend) { - ptr[2] = base64[(i >> 6) & 0x3F]; + ptr[2] = base64enc[(i >> 6) & 0x3F]; st->bufbeg++; } cursize += 4; @@ -1167,14 +1167,16 @@ static void mime_subparts_unbind(void *ptr) void Curl_mime_cleanpart(curl_mimepart *part) { - cleanup_part_content(part); - curl_slist_free_all(part->curlheaders); - if(part->flags & MIME_USERHEADERS_OWNER) - curl_slist_free_all(part->userheaders); - Curl_safefree(part->mimetype); - Curl_safefree(part->name); - Curl_safefree(part->filename); - Curl_mime_initpart(part); + if(part) { + cleanup_part_content(part); + curl_slist_free_all(part->curlheaders); + if(part->flags & MIME_USERHEADERS_OWNER) + curl_slist_free_all(part->userheaders); + Curl_safefree(part->mimetype); + Curl_safefree(part->name); + Curl_safefree(part->filename); + Curl_mime_initpart(part); + } } /* Recursively delete a mime handle and its parts. */ diff --git a/vendor/curl/lib/mqtt.c b/vendor/curl/lib/mqtt.c index dbe72398ad..30edf3dd8a 100644 --- a/vendor/curl/lib/mqtt.c +++ b/vendor/curl/lib/mqtt.c @@ -117,11 +117,9 @@ static CURLcode mqtt_send(struct Curl_easy *data, char *buf, size_t len) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct MQTT *mq = data->req.p.mqtt; ssize_t n; - result = Curl_write(data, sockfd, buf, len, &n); + result = Curl_nwrite(data, FIRSTSOCKET, buf, len, &n); if(result) return result; Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); @@ -636,7 +634,7 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) /* -- switched state -- */ remlen = mq->remaining_length; - infof(data, "Remaining length: %zd bytes", remlen); + infof(data, "Remaining length: %zu bytes", remlen); if(data->set.max_filesize && (curl_off_t)remlen > data->set.max_filesize) { failf(data, "Maximum file size exceeded"); diff --git a/vendor/curl/lib/multi.c b/vendor/curl/lib/multi.c index d1d32b7936..bb57dbceee 100644 --- a/vendor/curl/lib/multi.c +++ b/vendor/curl/lib/multi.c @@ -112,7 +112,7 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, static void process_pending_handles(struct Curl_multi *multi); #ifdef DEBUGBUILD -static const char * const statename[]={ +static const char * const multi_statename[]={ "INIT", "PENDING", "CONNECT", @@ -194,15 +194,10 @@ static void mstate(struct Curl_easy *data, CURLMstate state #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(data->mstate >= MSTATE_PENDING && data->mstate < MSTATE_COMPLETED) { - long connection_id = -5000; - - if(data->conn) - connection_id = data->conn->connection_id; - infof(data, - "STATE: %s => %s handle %p; line %d (connection #%ld)", - statename[oldstate], statename[data->mstate], - (void *)data, lineno, connection_id); + "STATE: %s => %s handle %p; line %d", + multi_statename[oldstate], multi_statename[data->mstate], + (void *)data, lineno); } #endif @@ -464,6 +459,20 @@ struct Curl_multi *curl_multi_init(void) CURL_DNS_HASH_SIZE); } +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) +{ + if(!multi->warned) { + infof(data, "!!! WARNING !!!"); + infof(data, "This is a debug build of libcurl, " + "do not use in production."); + multi->warned = true; + } +} +#else +#define multi_warn_debug(x,y) Curl_nop_stmt +#endif + /* returns TRUE if the easy handle is supposed to be present in the main link list */ static bool in_main_list(struct Curl_easy *data) @@ -623,8 +632,13 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->set.server_response_timeout; data->state.conn_cache->closure_handle->set.no_signal = data->set.no_signal; + data->id = data->state.conn_cache->next_easy_id++; + if(data->state.conn_cache->next_easy_id <= 0) + data->state.conn_cache->next_easy_id = 0; CONNCACHE_UNLOCK(data); + multi_warn_debug(multi, data); + return CURLM_OK; } @@ -653,8 +667,14 @@ static CURLcode multi_done(struct Curl_easy *data, struct connectdata *conn = data->conn; unsigned int i; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d", + multi_statename[data->mstate], + (int)status, (int)premature, data->state.done)); +#else DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d", (int)status, (int)premature, data->state.done)); +#endif if(data->state.done) /* Stop if multi_done() has already been called */ @@ -737,11 +757,12 @@ static CURLcode multi_done(struct Curl_easy *data, if premature is TRUE, it means this connection was said to be DONE before the entire request operation is complete and thus we can't know in what - state it is for re-using, so we're forced to close it. In a perfect world + state it is for reusing, so we're forced to close it. In a perfect world we can add code that keep track of if we really must close it here or not, but currently we have no such detail knowledge. */ + data->state.recent_conn_id = conn->connection_id; if((data->set.reuse_forbid #if defined(USE_NTLM) && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || @@ -753,8 +774,9 @@ static CURLcode multi_done(struct Curl_easy *data, #endif ) || conn->bits.close || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { - DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d" - ", close=%d, premature=%d, conn_multiplex=%d", + DEBUGF(infof(data, "multi_done, not reusing connection=%" + CURL_FORMAT_CURL_OFF_T ", forbid=%d" + ", close=%d, premature=%d, conn_multiplex=%d", conn->connection_id, data->set.reuse_forbid, conn->bits.close, premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET))); @@ -774,15 +796,16 @@ static CURLcode multi_done(struct Curl_easy *data, conn->bits.conn_to_host ? conn->conn_to_host.dispname : conn->host.dispname; /* create string before returning the connection */ - long connection_id = conn->connection_id; + curl_off_t connection_id = conn->connection_id; msnprintf(buffer, sizeof(buffer), - "Connection #%ld to host %s left intact", + "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact", connection_id, host); /* the connection is no longer in use by this transfer */ CONNCACHE_UNLOCK(data); if(Curl_conncache_return_conn(data, conn)) { /* remember the most recently used connection */ data->state.lastconnect_id = connection_id; + data->state.recent_conn_id = connection_id; infof(data, "%s", buffer); } else @@ -1087,8 +1110,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { int bitmap; #ifdef __clang_analyzer_ /* to prevent "The left operand of '>=' is a garbage value" warnings */ @@ -1097,30 +1119,21 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, bitmap = multi_getsock(data, sockbunch); for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) { + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { if(!FDSET_SOCK(sockbunch[i])) /* pretend it doesn't exist */ continue; - FD_SET(sockbunch[i], read_fd_set); - s = sockbunch[i]; + if(bitmap & GETSOCK_READSOCK(i)) + FD_SET(sockbunch[i], read_fd_set); + if(bitmap & GETSOCK_WRITESOCK(i)) + FD_SET(sockbunch[i], write_fd_set); + if((int)sockbunch[i] > this_max_fd) + this_max_fd = (int)sockbunch[i]; } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - FD_SET(sockbunch[i], write_fd_set); - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) - /* this socket is unused, break out of loop */ + else { break; - if((int)s > this_max_fd) - this_max_fd = (int)s; + } } - - data = data->next; /* check next handle */ } *max_fd = this_max_fd; @@ -1183,27 +1196,17 @@ static CURLMcode multi_wait(struct Curl_multi *multi, return CURLM_BAD_FUNCTION_ARGUMENT; /* Count up how many fds we have from the multi handle */ - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { ++nfds; - s = sockbunch[i]; } - if(s == CURL_SOCKET_BAD) { + else { break; } } - - data = data->next; /* check next handle */ } /* If the internally desired timeout is actually shorter than requested from @@ -1243,49 +1246,42 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { /* Add the curl handles to our pollfds first */ - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { + struct pollfd *ufd = &ufds[nfds++]; #ifdef USE_WINSOCK - long mask = 0; + long mask = 0; #endif - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; + ufd->fd = sockbunch[i]; + ufd->events = 0; + if(bitmap & GETSOCK_READSOCK(i)) { #ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; #endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLIN; - ++nfds; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; + ufd->events |= POLLIN; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { +#ifdef USE_WINSOCK + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(sockbunch[i]); +#endif + ufd->events |= POLLOUT; + } #ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(s); + if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) { + if(ufds_malloc) + free(ufds); + return CURLM_INTERNAL_ERROR; + } #endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLOUT; - ++nfds; } - /* s is only set if either being readable or writable is checked */ - if(s == CURL_SOCKET_BAD) { - /* break on entry not checked for being readable or writable */ + else { break; } -#ifdef USE_WINSOCK - if(WSAEventSelect(s, multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } -#endif } - - data = data->next; /* check next handle */ } } @@ -1394,8 +1390,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi, /* Count up all our own sockets that had activity, and remove them from the event. */ if(curlfds) { - data = multi->easyp; - while(data) { + + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { @@ -1412,8 +1408,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, break; } } - - data = data->next; } } @@ -1559,6 +1553,18 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear) return retval; } +/* + * Curl_multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (multiplexing become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. + */ +void Curl_multi_connchanged(struct Curl_multi *multi) +{ + multi->recheckstate = TRUE; +} + CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn) @@ -1593,7 +1599,6 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) DEBUGASSERT(conn->handler); if(conn->handler->do_it) - /* generic protocol-specific function pointer set in curl_connect() */ result = conn->handler->do_it(data, done); return result; @@ -1769,9 +1774,8 @@ static CURLcode protocol_connect(struct Curl_easy *data, */ static CURLcode readrewind(struct Curl_easy *data) { - struct connectdata *conn = data->conn; curl_mimepart *mimepart = &data->set.mimepost; - DEBUGASSERT(conn); + DEBUGASSERT(data->conn); data->state.rewindbeforesend = FALSE; /* we rewind now */ @@ -1784,12 +1788,12 @@ static CURLcode readrewind(struct Curl_easy *data) /* We have sent away data. If not using CURLOPT_POSTFIELDS or CURLOPT_HTTPPOST, call app to rewind */ - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - struct HTTP *http = data->req.p.http; - - if(http->sendit) - mimepart = http->sendit; +#ifndef CURL_DISABLE_HTTP + if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + if(data->state.mimepost) + mimepart = data->state.mimepost; } +#endif if(data->set.postfields || (data->state.httpreq == HTTPREQ_GET) || (data->state.httpreq == HTTPREQ_HEAD)) @@ -1895,14 +1899,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, MSTATE_COMPLETED); } -#ifdef DEBUGBUILD - if(!multi->warned) { - infof(data, "!!! WARNING !!!"); - infof(data, "This is a debug build of libcurl, " - "do not use in production."); - multi->warned = true; - } -#endif + multi_warn_debug(multi, data); do { /* A "stream" here is a logical stream if the protocol can handle that @@ -2449,7 +2446,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race - * condition and the server closed the re-used connection exactly when + * condition and the server closed the reused connection exactly when * we wanted to use it, so figure out if that is indeed the case. */ CURLcode ret = Curl_retry_request(data, &newurl); @@ -2491,7 +2488,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(result) { /* * The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is because we can't possibly + * closed to prevent being reused. This is because we can't possibly * know if the connection is in a good shape or not now. Unless it is * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. @@ -2922,7 +2919,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* walk over the sockets we got right now */ for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); + (curraction & GETSOCK_MASK_RW(i)); i++) { unsigned char action = CURL_POLL_NONE; unsigned char prevaction = 0; @@ -3690,7 +3687,7 @@ void Curl_expire_clear(struct Curl_easy *data) } #ifdef DEBUGBUILD - infof(data, "Expire cleared (transfer %p)", data); + infof(data, "Expire cleared"); #endif nowp->tv_sec = 0; nowp->tv_usec = 0; @@ -3798,7 +3795,7 @@ void Curl_multi_dump(struct Curl_multi *multi) /* only display handles that are not completed */ fprintf(stderr, "handle %p, state %s, %d sockets\n", (void *)data, - statename[data->mstate], data->numsocks); + multi_statename[data->mstate], data->numsocks); for(i = 0; i < data->numsocks; i++) { curl_socket_t s = data->sockets[i]; struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); diff --git a/vendor/curl/lib/multiif.h b/vendor/curl/lib/multiif.h index cae02cb08c..7f08ecc614 100644 --- a/vendor/curl/lib/multiif.h +++ b/vendor/curl/lib/multiif.h @@ -41,6 +41,8 @@ void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *easy); CURLcode Curl_preconnect(struct Curl_easy *data); +void Curl_multi_connchanged(struct Curl_multi *multi); + /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, @@ -57,6 +59,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, /* set the bit for the given sock number to make the bitmap for readable */ #define GETSOCK_READSOCK(x) (1 << (x)) +/* mask for checking if read and/or write is set for index x */ +#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x)) + #ifdef DEBUGBUILD /* * Curl_multi_dump is not a stable public function, this is only meant to diff --git a/vendor/curl/lib/pingpong.c b/vendor/curl/lib/pingpong.c index f3f7cb93cb..136985fc92 100644 --- a/vendor/curl/lib/pingpong.c +++ b/vendor/curl/lib/pingpong.c @@ -204,8 +204,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_write(data, conn->sock[FIRSTSOCKET], s, write_len, - &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written); if(result) return result; #ifdef HAVE_GSSAPI @@ -341,7 +340,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, ssize_t clipamount = 0; bool restart = FALSE; - data->req.headerbytecount += (long)gotbytes; + data->req.headerbytecount += (unsigned int)gotbytes; pp->nread_resp += gotbytes; for(i = 0; i < gotbytes; ptr++, i++) { @@ -467,11 +466,10 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, struct pingpong *pp) { /* we have a piece of a command still left to send */ - struct connectdata *conn = data->conn; ssize_t written; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - CURLcode result = Curl_write(data, sock, pp->sendthis + pp->sendsize - - pp->sendleft, pp->sendleft, &written); + CURLcode result = Curl_nwrite(data, FIRSTSOCKET, + pp->sendthis + pp->sendsize - pp->sendleft, + pp->sendleft, &written); if(result) return result; diff --git a/vendor/curl/lib/pop3.c b/vendor/curl/lib/pop3.c index 0de34cc113..a9d5fdd698 100644 --- a/vendor/curl/lib/pop3.c +++ b/vendor/curl/lib/pop3.c @@ -47,9 +47,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -282,11 +279,11 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * state() + * pop3_state() * * This is the ONLY way to change POP3 state! */ -static void state(struct Curl_easy *data, pop3state newstate) +static void pop3_state(struct Curl_easy *data, pop3state newstate) { struct pop3_conn *pop3c = &data->conn->proto.pop3c; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -335,7 +332,7 @@ static CURLcode pop3_perform_capa(struct Curl_easy *data, result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA"); if(!result) - state(data, POP3_CAPA); + pop3_state(data, POP3_CAPA); return result; } @@ -353,7 +350,7 @@ static CURLcode pop3_perform_starttls(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS"); if(!result) - state(data, POP3_STARTTLS); + pop3_state(data, POP3_STARTTLS); return result; } @@ -383,7 +380,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, if(!result) { pop3c->ssldone = ssldone; if(pop3c->state != POP3_UPGRADETLS) - state(data, POP3_UPGRADETLS); + pop3_state(data, POP3_UPGRADETLS); if(pop3c->ssldone) { pop3_to_pop3s(conn); @@ -408,7 +405,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 don't */ if(!data->state.aptr.user) { - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -417,12 +414,12 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s", conn->user ? conn->user : ""); if(!result) - state(data, POP3_USER); + pop3_state(data, POP3_USER); return result; } -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH /*********************************************************************** * * pop3_perform_apop() @@ -442,7 +439,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 don't */ if(!data->state.aptr.user) { - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -468,7 +465,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); if(!result) - state(data, POP3_APOP); + pop3_state(data, POP3_APOP); return result; } @@ -552,7 +549,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, /* Check we have enough data to authenticate with and end the connect phase if we don't */ if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -562,11 +559,11 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, if(!result) if(progress == SASL_INPROGRESS) - state(data, POP3_AUTH); + pop3_state(data, POP3_AUTH); } if(!result && progress == SASL_IDLE) { -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) /* Perform APOP authentication */ result = pop3_perform_apop(data, conn); @@ -620,7 +617,7 @@ static CURLcode pop3_perform_command(struct Curl_easy *data) pop3->custom : command)); if(!result) - state(data, POP3_COMMAND); + pop3_state(data, POP3_COMMAND); return result; } @@ -638,7 +635,7 @@ static CURLcode pop3_perform_quit(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT"); if(!result) - state(data, POP3_QUIT); + pop3_state(data, POP3_QUIT); return result; } @@ -831,10 +828,10 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - state(data, POP3_STOP); /* Authenticated */ + pop3_state(data, POP3_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) /* Perform APOP authentication */ result = pop3_perform_apop(data, conn); @@ -855,7 +852,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, return result; } -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH /* For APOP responses */ static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, pop3state instate) @@ -869,7 +866,7 @@ static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, } else /* End of connect phase */ - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -892,7 +889,7 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s", conn->passwd ? conn->passwd : ""); if(!result) - state(data, POP3_PASS); + pop3_state(data, POP3_PASS); return result; } @@ -910,7 +907,7 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, } else /* End of connect phase */ - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -929,7 +926,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ if(pop3code != '+') { - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return CURLE_WEIRD_SERVER_REPLY; } @@ -967,7 +964,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, } /* End of DO phase */ - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); return result; } @@ -1018,7 +1015,7 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, result = pop3_state_auth_resp(data, pop3code, pop3c->state); break; -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH case POP3_APOP: result = pop3_state_apop_resp(data, pop3code, pop3c->state); break; @@ -1037,12 +1034,12 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, break; case POP3_QUIT: - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); break; default: /* internal error */ - state(data, POP3_STOP); + pop3_state(data, POP3_STOP); break; } } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); @@ -1143,7 +1140,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - state(data, POP3_SERVERGREET); + pop3_state(data, POP3_SERVERGREET); result = pop3_multi_statemach(data, done); diff --git a/vendor/curl/lib/rand.c b/vendor/curl/lib/rand.c index 7d24765ccd..a5620ea6eb 100644 --- a/vendor/curl/lib/rand.c +++ b/vendor/curl/lib/rand.c @@ -36,6 +36,7 @@ uint32_t arc4random(void); #endif #include +#include "urldata.h" #include "vtls/vtls.h" #include "sendf.h" #include "timeval.h" @@ -187,7 +188,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) * 'rnd' points to. * * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (rustls, Gskit or mbedTLS), this function will use "weak" + * proper random API (rustls or mbedTLS), this function will use "weak" * random. * * When built *with* TLS support and a backend that offers strong random, it diff --git a/vendor/curl/lib/rand.h b/vendor/curl/lib/rand.h index cbe05677a1..45ce3e7c4e 100644 --- a/vendor/curl/lib/rand.h +++ b/vendor/curl/lib/rand.h @@ -24,20 +24,6 @@ * ***************************************************************************/ -/* - * Curl_rand() stores 'num' number of random unsigned characters in the buffer - * 'rnd' points to. - * - * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (Gskit or mbedTLS), this function will use "weak" random. - * - * When built *with* TLS support and a backend that offers strong random, it - * will return error if it cannot provide strong random values. - * - * NOTE: 'data' may be passed in as NULL when coming from external API without - * easy handle! - * - */ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num); /* diff --git a/vendor/curl/lib/sendf.c b/vendor/curl/lib/sendf.c index 81ee864826..d79ad08fa5 100644 --- a/vendor/curl/lib/sendf.c +++ b/vendor/curl/lib/sendf.c @@ -139,27 +139,27 @@ static size_t convert_lineends(struct Curl_easy *data, #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ /* - * Curl_write() is an internal write function that sends data to the - * server. Works with plain sockets, SCP, SSL or kerberos. + * Curl_nwrite() is an internal write function that sends data to the + * server. Works with a socket index for the connection. * - * If the write would block (CURLE_AGAIN), we return CURLE_OK and - * (*written == 0). Otherwise we return regular CURLcode value. + * If the write would block (CURLE_AGAIN), it returns CURLE_OK and + * (*nwritten == 0). Otherwise we return regular CURLcode value. */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) +CURLcode Curl_nwrite(struct Curl_easy *data, + int sockindex, + const void *buf, + size_t blen, + ssize_t *pnwritten) { - ssize_t bytes_written; + ssize_t nwritten; CURLcode result = CURLE_OK; struct connectdata *conn; - int num; + + DEBUGASSERT(sockindex >= 0 && sockindex < 2); + DEBUGASSERT(pnwritten); DEBUGASSERT(data); DEBUGASSERT(data->conn); conn = data->conn; - num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); - #ifdef CURLDEBUG { /* Allow debug builds to override this logic to force short sends @@ -168,31 +168,47 @@ CURLcode Curl_write(struct Curl_easy *data, if(p) { size_t altsize = (size_t)strtoul(p, NULL, 10); if(altsize) - len = CURLMIN(len, altsize); + blen = CURLMIN(blen, altsize); } } #endif - bytes_written = conn->send[num](data, num, mem, len, &result); - - *written = bytes_written; - if(bytes_written >= 0) - /* we completely ignore the curlcode value when subzero is not returned */ - return CURLE_OK; + nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); + if(result == CURLE_AGAIN) { + nwritten = 0; + result = CURLE_OK; + } + else if(result) { + nwritten = -1; /* make sure */ + } + else { + DEBUGASSERT(nwritten >= 0); + } - /* handle CURLE_AGAIN or a send failure */ - switch(result) { - case CURLE_AGAIN: - *written = 0; - return CURLE_OK; + *pnwritten = nwritten; + return result; +} - case CURLE_OK: - /* general send failure */ - return CURLE_SEND_ERROR; +/* + * Curl_write() is an internal write function that sends data to the + * server. Works with plain sockets, SCP, SSL or kerberos. + * + * If the write would block (CURLE_AGAIN), we return CURLE_OK and + * (*written == 0). Otherwise we return regular CURLcode value. + */ +CURLcode Curl_write(struct Curl_easy *data, + curl_socket_t sockfd, + const void *mem, + size_t len, + ssize_t *written) +{ + struct connectdata *conn; + int num; - default: - /* we got a specific curlcode, forward it */ - return result; - } + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; + num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); + return Curl_nwrite(data, num, mem, len, written); } static CURLcode pausewrite(struct Curl_easy *data, @@ -419,8 +435,5 @@ CURLcode Curl_read(struct Curl_easy *data, /* transfer */ *n += nread; result = CURLE_OK; out: - /* DEBUGF(infof(data, "Curl_read(handle=%p) -> %d, nread=%ld", - data, result, nread)); */ return result; } - diff --git a/vendor/curl/lib/sendf.h b/vendor/curl/lib/sendf.h index d0c9275705..5ef2b91df7 100644 --- a/vendor/curl/lib/sendf.h +++ b/vendor/curl/lib/sendf.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#include "curl_log.h" +#include "curl_trc.h" #define CLIENTWRITE_BODY (1<<0) @@ -51,4 +51,11 @@ CURLcode Curl_write(struct Curl_easy *data, const void *mem, size_t len, ssize_t *written); +/* internal write-function, using sockindex for connection destination */ +CURLcode Curl_nwrite(struct Curl_easy *data, + int sockindex, + const void *buf, + size_t blen, + ssize_t *pnwritten); + #endif /* HEADER_CURL_SENDF_H */ diff --git a/vendor/curl/lib/setopt.c b/vendor/curl/lib/setopt.c index 0c3b9634d1..2cef1b3d82 100644 --- a/vendor/curl/lib/setopt.c +++ b/vendor/curl/lib/setopt.c @@ -666,17 +666,20 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.method = HTTPREQ_GET; break; -#ifndef CURL_DISABLE_MIME +#ifndef CURL_DISABLE_FORM_API case CURLOPT_HTTPPOST: /* - * Set to make us do HTTP POST + * Set to make us do HTTP POST. Legacy API-style. */ data->set.httppost = va_arg(param, struct curl_httppost *); data->set.method = HTTPREQ_POST_FORM; data->set.opt_no_body = FALSE; /* this is implied */ + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); break; #endif +#if !defined(CURL_DISABLE_AWS) case CURLOPT_AWS_SIGV4: /* * String that is merged to some authentication @@ -690,6 +693,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(data->set.str[STRING_AWS_SIGV4]) data->set.httpauth = CURLAUTH_AWS_SIGV4; break; +#endif case CURLOPT_REFERER: /* @@ -985,6 +989,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(!result) { data->set.method = HTTPREQ_POST_MIME; data->set.opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); +#endif } break; @@ -1237,6 +1245,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.out = va_arg(param, void *); break; +#ifdef CURL_LIST_ONLY_PROTOCOL case CURLOPT_DIRLISTONLY: /* * An option that changes the command to one that asks for a list only, no @@ -1244,7 +1253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; break; - +#endif case CURLOPT_APPEND: /* * We want to upload and append to an existing file. Used for FTP and @@ -1867,6 +1876,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; break; + case CURLOPT_HAPROXY_CLIENT_IP: + /* + * Set the client IP to send through HAProxy PROXY protocol + */ + result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP], + va_arg(param, char *)); + /* We enable implicitly the HAProxy protocol if we use this flag. */ + data->set.haproxyprotocol = TRUE; + break; #endif case CURLOPT_INTERFACE: /* @@ -1876,6 +1894,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_DEVICE], va_arg(param, char *)); break; +#ifndef CURL_DISABLE_BINDLOCAL case CURLOPT_LOCALPORT: /* * Set what local port to bind the socket to when performing an operation. @@ -1894,6 +1913,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.localportrange = curlx_sltous(arg); break; +#endif case CURLOPT_GSSAPI_DELEGATION: /* * GSS-API credential delegation bitmask @@ -2711,7 +2731,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Set the list of mail recipients */ data->set.mail_rcpt = va_arg(param, struct curl_slist *); break; - case CURLOPT_MAIL_RCPT_ALLLOWFAILS: + case CURLOPT_MAIL_RCPT_ALLOWFAILS: /* allow RCPT TO command to fail for some recipients */ data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; break; diff --git a/vendor/curl/lib/setup-os400.h b/vendor/curl/lib/setup-os400.h index 759583466c..53e91777ec 100644 --- a/vendor/curl/lib/setup-os400.h +++ b/vendor/curl/lib/setup-os400.h @@ -34,6 +34,9 @@ /* No OS/400 header file defines u_int32_t. */ typedef unsigned long u_int32_t; +/* OS/400 has no idea of a tty! */ +#define isatty(fd) 0 + /* System API wrapper prototypes & definitions to support ASCII parameters. */ @@ -57,94 +60,6 @@ extern int Curl_getnameinfo_a(const struct sockaddr *sa, int flags); #define getnameinfo Curl_getnameinfo_a - -/* GSKit wrappers. */ - -extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); -#define gsk_environment_open Curl_gsk_environment_open - -extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, - gsk_handle * my_session_handle); -#define gsk_secure_soc_open Curl_gsk_secure_soc_open - -extern int Curl_gsk_environment_close(gsk_handle * my_env_handle); -#define gsk_environment_close Curl_gsk_environment_close - -extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle); -#define gsk_secure_soc_close Curl_gsk_secure_soc_close - -extern int Curl_gsk_environment_init(gsk_handle my_env_handle); -#define gsk_environment_init Curl_gsk_environment_init - -extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle); -#define gsk_secure_soc_init Curl_gsk_secure_soc_init - -extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char *buffer, - int bufSize); -#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a - -extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE enumValue); -#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum - -extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int numValue); -#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value - -extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, - GSK_CALLBACK_ID callBackID, - void *callBackAreaPtr); -#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback - -extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char **buffer, - int *bufSize); -#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a - -extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE *enumValue); -#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum - -extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int *numValue); -#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value - -extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, - GSK_CERT_ID certID, - const gsk_cert_data_elem **certDataElem, - int *certDataElementCount); -#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info - -extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, - GSK_MISC_ID miscID); -#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc - -extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle, - char *readBuffer, - int readBufSize, int *amtRead); -#define gsk_secure_soc_read Curl_gsk_secure_soc_read - -extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle, - char *writeBuffer, - int writeBufSize, int *amtWritten); -#define gsk_secure_soc_write Curl_gsk_secure_soc_write - -extern const char * Curl_gsk_strerror_a(int gsk_return_value); -#define gsk_strerror Curl_gsk_strerror_a - -extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, - int IOCompletionPort, - Qso_OverlappedIO_t * communicationsArea); -#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit - - /* GSSAPI wrappers. */ extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, diff --git a/vendor/curl/lib/setup-vms.h b/vendor/curl/lib/setup-vms.h index 46657b2cd4..645cc1a9cd 100644 --- a/vendor/curl/lib/setup-vms.h +++ b/vendor/curl/lib/setup-vms.h @@ -262,7 +262,6 @@ static struct passwd *vms_getpwuid(uid_t uid) #define PKCS12_parse PKCS12_PARSE #define RAND_add RAND_ADD #define RAND_bytes RAND_BYTES -#define RAND_egd RAND_EGD #define RAND_file_name RAND_FILE_NAME #define RAND_load_file RAND_LOAD_FILE #define RAND_status RAND_STATUS diff --git a/vendor/curl/lib/sha256.c b/vendor/curl/lib/sha256.c index 767d879c45..4a02045d26 100644 --- a/vendor/curl/lib/sha256.c +++ b/vendor/curl/lib/sha256.c @@ -25,7 +25,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) #include "warnless.h" #include "curl_sha256.h" @@ -110,7 +111,10 @@ static CURLcode my_sha256_init(my_sha256_ctx *ctx) if(!ctx->openssl_ctx) return CURLE_OUT_OF_MEMORY; - EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL); + if(!EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL)) { + EVP_MD_CTX_destroy(ctx->openssl_ctx); + return CURLE_FAILED_INIT; + } return CURLE_OK; } @@ -218,9 +222,14 @@ typedef struct sha256_ctx my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) { - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_OUT_OF_MEMORY; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; } return CURLE_OK; @@ -533,4 +542,4 @@ const struct HMAC_params Curl_HMAC_SHA256[] = { }; -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* AWS, DIGEST, or libSSH2 */ diff --git a/vendor/curl/lib/smb.c b/vendor/curl/lib/smb.c index d682221352..afcc99de24 100644 --- a/vendor/curl/lib/smb.c +++ b/vendor/curl/lib/smb.c @@ -27,8 +27,6 @@ #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) -#define BUILDING_CURL_SMB_C - #ifdef WIN32 #define getpid GetCurrentProcessId #endif @@ -50,6 +48,199 @@ #include "curl_memory.h" #include "memdebug.h" +/* + * Definitions for SMB protocol data structures + */ +#if defined(_MSC_VER) || defined(__ILEC400__) +# define PACK +# pragma pack(push) +# pragma pack(1) +#elif defined(__GNUC__) +# define PACK __attribute__((packed)) +#else +# define PACK +#endif + +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_READ_ANDX 0x2e +#define SMB_COM_WRITE_ANDX 0x2f +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SETUP_ANDX 0x73 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_CREATE_ANDX 0xa2 +#define SMB_COM_NO_ANDX_COMMAND 0xff + +#define SMB_WC_CLOSE 0x03 +#define SMB_WC_READ_ANDX 0x0c +#define SMB_WC_WRITE_ANDX 0x0e +#define SMB_WC_SETUP_ANDX 0x0d +#define SMB_WC_TREE_CONNECT_ANDX 0x04 +#define SMB_WC_NT_CREATE_ANDX 0x18 + +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 +#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 + +#define SMB_CAP_LARGE_FILES 0x08 +#define SMB_GENERIC_WRITE 0x40000000 +#define SMB_GENERIC_READ 0x80000000 +#define SMB_FILE_SHARE_ALL 0x07 +#define SMB_FILE_OPEN 0x01 +#define SMB_FILE_OVERWRITE_IF 0x05 + +#define SMB_ERR_NOACCESS 0x00050001 + +struct smb_header { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned short nbt_length; + unsigned char magic[4]; + unsigned char command; + unsigned int status; + unsigned char flags; + unsigned short flags2; + unsigned short pid_high; + unsigned char signature[8]; + unsigned short pad; + unsigned short tid; + unsigned short pid; + unsigned short uid; + unsigned short mid; +} PACK; + +struct smb_negotiate_response { + struct smb_header h; + unsigned char word_count; + unsigned short dialect_index; + unsigned char security_mode; + unsigned short max_mpx_count; + unsigned short max_number_vcs; + unsigned int max_buffer_size; + unsigned int max_raw_size; + unsigned int session_key; + unsigned int capabilities; + unsigned int system_time_low; + unsigned int system_time_high; + unsigned short server_time_zone; + unsigned char encryption_key_length; + unsigned short byte_count; + char bytes[1]; +} PACK; + +struct andx { + unsigned char command; + unsigned char pad; + unsigned short offset; +} PACK; + +struct smb_setup { + unsigned char word_count; + struct andx andx; + unsigned short max_buffer_size; + unsigned short max_mpx_count; + unsigned short vc_number; + unsigned int session_key; + unsigned short lengths[2]; + unsigned int pad; + unsigned int capabilities; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_tree_connect { + unsigned char word_count; + struct andx andx; + unsigned short flags; + unsigned short pw_len; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create { + unsigned char word_count; + struct andx andx; + unsigned char pad; + unsigned short name_length; + unsigned int flags; + unsigned int root_fid; + unsigned int access; + curl_off_t allocation_size; + unsigned int ext_file_attributes; + unsigned int share_access; + unsigned int create_disposition; + unsigned int create_options; + unsigned int impersonation_level; + unsigned char security_flags; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create_response { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned char op_lock_level; + unsigned short fid; + unsigned int create_disposition; + + curl_off_t create_time; + curl_off_t last_access_time; + curl_off_t last_write_time; + curl_off_t last_change_time; + unsigned int ext_file_attributes; + curl_off_t allocation_size; + curl_off_t end_of_file; +} PACK; + +struct smb_read { + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned short max_bytes; + unsigned short min_bytes; + unsigned int timeout; + unsigned short remaining; + unsigned int offset_high; + unsigned short byte_count; +} PACK; + +struct smb_write { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned int timeout; + unsigned short write_mode; + unsigned short remaining; + unsigned short pad; + unsigned short data_length; + unsigned short data_offset; + unsigned int offset_high; + unsigned short byte_count; + unsigned char pad2; +} PACK; + +struct smb_close { + unsigned char word_count; + unsigned short fid; + unsigned int last_mtime; + unsigned short byte_count; +} PACK; + +struct smb_tree_disconnect { + unsigned char word_count; + unsigned short byte_count; +} PACK; + +#if defined(_MSC_VER) || defined(__ILEC400__) +# pragma pack(pop) +#endif + /* Local API functions */ static CURLcode smb_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -373,12 +564,11 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, size_t upload_size) { struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; CURLcode result; - result = Curl_write(data, sockfd, data->state.ulbuf, + result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf, len, &bytes_written); if(result) return result; @@ -396,7 +586,6 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, static CURLcode smb_flush(struct Curl_easy *data) { struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; ssize_t len = smbc->send_size - smbc->sent; @@ -405,9 +594,9 @@ static CURLcode smb_flush(struct Curl_easy *data) if(!smbc->send_size) return CURLE_OK; - result = Curl_write(data, sockfd, - data->state.ulbuf + smbc->sent, - len, &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, + data->state.ulbuf + smbc->sent, + len, &bytes_written); if(result) return result; diff --git a/vendor/curl/lib/smb.h b/vendor/curl/lib/smb.h index c35f3e9700..437f4a58a8 100644 --- a/vendor/curl/lib/smb.h +++ b/vendor/curl/lib/smb.h @@ -48,203 +48,6 @@ struct smb_conn { size_t got; }; -/* - * Definitions for SMB protocol data structures - */ -#ifdef BUILDING_CURL_SMB_C - -#if defined(_MSC_VER) || defined(__ILEC400__) -# define PACK -# pragma pack(push) -# pragma pack(1) -#elif defined(__GNUC__) -# define PACK __attribute__((packed)) -#else -# define PACK -#endif - -#define SMB_COM_CLOSE 0x04 -#define SMB_COM_READ_ANDX 0x2e -#define SMB_COM_WRITE_ANDX 0x2f -#define SMB_COM_TREE_DISCONNECT 0x71 -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SETUP_ANDX 0x73 -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#define SMB_COM_NT_CREATE_ANDX 0xa2 -#define SMB_COM_NO_ANDX_COMMAND 0xff - -#define SMB_WC_CLOSE 0x03 -#define SMB_WC_READ_ANDX 0x0c -#define SMB_WC_WRITE_ANDX 0x0e -#define SMB_WC_SETUP_ANDX 0x0d -#define SMB_WC_TREE_CONNECT_ANDX 0x04 -#define SMB_WC_NT_CREATE_ANDX 0x18 - -#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 -#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 -#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 -#define SMB_FLAGS2_IS_LONG_NAME 0x0040 -#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 - -#define SMB_CAP_LARGE_FILES 0x08 -#define SMB_GENERIC_WRITE 0x40000000 -#define SMB_GENERIC_READ 0x80000000 -#define SMB_FILE_SHARE_ALL 0x07 -#define SMB_FILE_OPEN 0x01 -#define SMB_FILE_OVERWRITE_IF 0x05 - -#define SMB_ERR_NOACCESS 0x00050001 - -struct smb_header { - unsigned char nbt_type; - unsigned char nbt_flags; - unsigned short nbt_length; - unsigned char magic[4]; - unsigned char command; - unsigned int status; - unsigned char flags; - unsigned short flags2; - unsigned short pid_high; - unsigned char signature[8]; - unsigned short pad; - unsigned short tid; - unsigned short pid; - unsigned short uid; - unsigned short mid; -} PACK; - -struct smb_negotiate_response { - struct smb_header h; - unsigned char word_count; - unsigned short dialect_index; - unsigned char security_mode; - unsigned short max_mpx_count; - unsigned short max_number_vcs; - unsigned int max_buffer_size; - unsigned int max_raw_size; - unsigned int session_key; - unsigned int capabilities; - unsigned int system_time_low; - unsigned int system_time_high; - unsigned short server_time_zone; - unsigned char encryption_key_length; - unsigned short byte_count; - char bytes[1]; -} PACK; - -struct andx { - unsigned char command; - unsigned char pad; - unsigned short offset; -} PACK; - -struct smb_setup { - unsigned char word_count; - struct andx andx; - unsigned short max_buffer_size; - unsigned short max_mpx_count; - unsigned short vc_number; - unsigned int session_key; - unsigned short lengths[2]; - unsigned int pad; - unsigned int capabilities; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_tree_connect { - unsigned char word_count; - struct andx andx; - unsigned short flags; - unsigned short pw_len; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create { - unsigned char word_count; - struct andx andx; - unsigned char pad; - unsigned short name_length; - unsigned int flags; - unsigned int root_fid; - unsigned int access; - curl_off_t allocation_size; - unsigned int ext_file_attributes; - unsigned int share_access; - unsigned int create_disposition; - unsigned int create_options; - unsigned int impersonation_level; - unsigned char security_flags; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create_response { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned char op_lock_level; - unsigned short fid; - unsigned int create_disposition; - - curl_off_t create_time; - curl_off_t last_access_time; - curl_off_t last_write_time; - curl_off_t last_change_time; - unsigned int ext_file_attributes; - curl_off_t allocation_size; - curl_off_t end_of_file; -} PACK; - -struct smb_read { - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned short max_bytes; - unsigned short min_bytes; - unsigned int timeout; - unsigned short remaining; - unsigned int offset_high; - unsigned short byte_count; -} PACK; - -struct smb_write { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned int timeout; - unsigned short write_mode; - unsigned short remaining; - unsigned short pad; - unsigned short data_length; - unsigned short data_offset; - unsigned int offset_high; - unsigned short byte_count; - unsigned char pad2; -} PACK; - -struct smb_close { - unsigned char word_count; - unsigned short fid; - unsigned int last_mtime; - unsigned short byte_count; -} PACK; - -struct smb_tree_disconnect { - unsigned char word_count; - unsigned short byte_count; -} PACK; - -#if defined(_MSC_VER) || defined(__ILEC400__) -# pragma pack(pop) -#endif - -#endif /* BUILDING_CURL_SMB_C */ - #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ (SIZEOF_CURL_OFF_T > 4) diff --git a/vendor/curl/lib/smtp.c b/vendor/curl/lib/smtp.c index c182cace74..81a17e38db 100644 --- a/vendor/curl/lib/smtp.c +++ b/vendor/curl/lib/smtp.c @@ -49,9 +49,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -281,11 +278,11 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) /*********************************************************************** * - * state() + * smtp_state() * * This is the ONLY way to change SMTP state! */ -static void state(struct Curl_easy *data, smtpstate newstate) +static void smtp_state(struct Curl_easy *data, smtpstate newstate) { struct smtp_conn *smtpc = &data->conn->proto.smtpc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -338,7 +335,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data) result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); if(!result) - state(data, SMTP_EHLO); + smtp_state(data, SMTP_EHLO); return result; } @@ -362,7 +359,7 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data, result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain); if(!result) - state(data, SMTP_HELO); + smtp_state(data, SMTP_HELO); return result; } @@ -381,7 +378,7 @@ static CURLcode smtp_perform_starttls(struct Curl_easy *data, "%s", "STARTTLS"); if(!result) - state(data, SMTP_STARTTLS); + smtp_state(data, SMTP_STARTTLS); return result; } @@ -410,7 +407,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) if(!result) { smtpc->ssldone = ssldone; if(smtpc->state != SMTP_UPGRADETLS) - state(data, SMTP_UPGRADETLS); + smtp_state(data, SMTP_UPGRADETLS); if(smtpc->ssldone) { smtp_to_smtps(conn); @@ -499,7 +496,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) server supports authentication, and end the connect phase if not */ if(!smtpc->auth_supported || !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); return result; } @@ -508,7 +505,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) if(!result) { if(progress == SASL_INPROGRESS) - state(data, SMTP_AUTH); + smtp_state(data, SMTP_AUTH); else { /* Other mechanisms not supported */ infof(data, "No known authentication mechanisms supported"); @@ -586,7 +583,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) smtp->custom : "HELP"); if(!result) - state(data, SMTP_COMMAND); + smtp_state(data, SMTP_COMMAND); return result; } @@ -771,7 +768,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) free(size); if(!result) - state(data, SMTP_MAIL); + smtp_state(data, SMTP_MAIL); return result; } @@ -812,7 +809,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) free(address); if(!result) - state(data, SMTP_RCPT); + smtp_state(data, SMTP_RCPT); return result; } @@ -830,7 +827,7 @@ static CURLcode smtp_perform_quit(struct Curl_easy *data, CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT"); if(!result) - state(data, SMTP_QUIT); + smtp_state(data, SMTP_QUIT); return result; } @@ -996,7 +993,7 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode, } else /* End of connect phase */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); return result; } @@ -1017,7 +1014,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data, if(!result) switch(progress) { case SASL_DONE: - state(data, SMTP_STOP); /* Authenticated */ + smtp_state(data, SMTP_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ failf(data, "Authentication cancelled"); @@ -1064,11 +1061,11 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, } else /* End of DO phase */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); } else /* End of DO phase */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); } } @@ -1145,7 +1142,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA"); if(!result) - state(data, SMTP_DATA); + smtp_state(data, SMTP_DATA); } } } @@ -1172,7 +1169,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode, Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); } return result; @@ -1192,7 +1189,7 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; /* End of DONE phase */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); return result; } @@ -1274,7 +1271,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data, /* fallthrough, just stop! */ default: /* internal error */ - state(data, SMTP_STOP); + smtp_state(data, SMTP_STOP); break; } } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); @@ -1379,7 +1376,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) return result; /* Start off waiting for the server greeting response */ - state(data, SMTP_SERVERGREET); + smtp_state(data, SMTP_SERVERGREET); result = smtp_multi_statemach(data, done); @@ -1461,7 +1458,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, free(eob); } - state(data, SMTP_POSTDATA); + smtp_state(data, SMTP_POSTDATA); /* Run the state-machine */ result = smtp_block_statemach(data, conn, FALSE); diff --git a/vendor/curl/lib/socks.c b/vendor/curl/lib/socks.c index 53d798a64c..c492d663c4 100644 --- a/vendor/curl/lib/socks.c +++ b/vendor/curl/lib/socks.c @@ -161,7 +161,7 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, enum connect_t oldstate = sx->state; #ifdef DEBUG_AND_VERBOSE /* synced with the state list in urldata.h */ - static const char * const statename[] = { + static const char * const socks_statename[] = { "INIT", "SOCKS_INIT", "SOCKS_SEND", @@ -193,7 +193,7 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, #ifdef DEBUG_AND_VERBOSE infof(data, "SXSTATE: %s => %s; line %d", - statename[oldstate], statename[sx->state], + socks_statename[oldstate], socks_statename[sx->state], lineno); #endif } @@ -567,7 +567,6 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, */ struct connectdata *conn = cf->conn; unsigned char *socksreq = (unsigned char *)data->state.buffer; - char dest[256] = "unknown"; /* printable hostname:port */ int idx; CURLcode result; CURLproxycode presult; @@ -820,8 +819,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, /* FALLTHROUGH */ CONNECT_RESOLVED: case CONNECT_RESOLVED: { + char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */ struct Curl_addrinfo *hp = NULL; - size_t destlen; if(dns) hp = dns->addr; if(!hp) { @@ -831,8 +830,6 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, } Curl_printable_address(hp, dest, sizeof(dest)); - destlen = strlen(dest); - msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port); len = 0; socksreq[len++] = 5; /* version (SOCKS5) */ @@ -848,7 +845,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; } - infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest); + infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest, + sx->remote_port); } #ifdef ENABLE_IPV6 else if(hp->ai_family == AF_INET6) { @@ -862,7 +860,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; } - infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest); + infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest, + sx->remote_port); } #endif else { @@ -1115,7 +1114,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - result = cf->next->cft->connect(cf->next, data, blocking, done); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) return result; @@ -1193,7 +1192,7 @@ static void socks_proxy_cf_close(struct Curl_cfilter *cf, DEBUGASSERT(cf->next); cf->connected = FALSE; socks_proxy_cf_free(cf); - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); } static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, diff --git a/vendor/curl/lib/strerror.c b/vendor/curl/lib/strerror.c index bd9cc535c3..be41914140 100644 --- a/vendor/curl/lib/strerror.c +++ b/vendor/curl/lib/strerror.c @@ -938,7 +938,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) #ifndef CURL_DISABLE_VERBOSE_STRINGS if(!get_winapi_error(err, buf, buflen)) { - msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err); + msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err); } #else { diff --git a/vendor/curl/lib/system_win32.h b/vendor/curl/lib/system_win32.h index 24899cb2d5..6482643fab 100644 --- a/vendor/curl/lib/system_win32.h +++ b/vendor/curl/lib/system_win32.h @@ -42,7 +42,8 @@ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); - -#endif /* WIN32 */ +#else /* WIN32 */ +#define Curl_win32_init(x) CURLE_OK +#endif /* !WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/vendor/curl/lib/telnet.c b/vendor/curl/lib/telnet.c index 643e43d2b0..850f88c1ec 100644 --- a/vendor/curl/lib/telnet.c +++ b/vendor/curl/lib/telnet.c @@ -1266,10 +1266,10 @@ static CURLcode send_telnet_data(struct Curl_easy *data, break; default: /* write! */ bytes_written = 0; - result = Curl_write(data, conn->sock[FIRSTSOCKET], - outbuf + total_written, - outlen - total_written, - &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, + outbuf + total_written, + outlen - total_written, + &bytes_written); total_written += bytes_written; break; } @@ -1534,7 +1534,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } while(keepon) { - DEBUGF(infof(data, "telnet_do(handle=%p), poll %d fds", data, poll_cnt)); + DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt)); switch(Curl_poll(pfd, poll_cnt, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; @@ -1558,8 +1558,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) * in a clean way? Seems to be timing related, happens more * on slow debug build */ if(data->state.os_errno == ECONNRESET) { - DEBUGF(infof(data, "telnet_do(handle=%p), unexpected ECONNRESET" - " on recv", data)); + DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv")); } break; } diff --git a/vendor/curl/lib/timeval.c b/vendor/curl/lib/timeval.c index dca1c6fe50..2de79be46d 100644 --- a/vendor/curl/lib/timeval.c +++ b/vendor/curl/lib/timeval.c @@ -58,7 +58,8 @@ struct curltime Curl_now(void) return now; } -#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) +#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ + defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) struct curltime Curl_now(void) { @@ -87,6 +88,19 @@ struct curltime Curl_now(void) have_clock_gettime = TRUE; #endif +#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW + if( +#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ + (HAVE_BUILTIN_AVAILABLE == 1) + have_clock_gettime && +#endif + (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) { + cnow.tv_sec = tsnow.tv_sec; + cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); + } + else +#endif + if( #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ (HAVE_BUILTIN_AVAILABLE == 1) diff --git a/vendor/curl/lib/transfer.c b/vendor/curl/lib/transfer.c index d2ff0c24c2..d0602b8753 100644 --- a/vendor/curl/lib/transfer.c +++ b/vendor/curl/lib/transfer.c @@ -428,7 +428,10 @@ static CURLcode readwrite_data(struct Curl_easy *data, size_t excess = 0; /* excess bytes read */ bool readmore = FALSE; /* used by RTP to signal for more data */ int maxloops = 100; + curl_off_t max_recv = data->set.max_recv_speed? + data->set.max_recv_speed : CURL_OFF_T_MAX; char *buf = data->state.buffer; + bool data_eof_handled = FALSE; DEBUGASSERT(buf); *done = FALSE; @@ -446,8 +449,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, to ensure that http2_handle_stream_close is called when we read all incoming bytes for a particular stream. */ bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); - bool data_eof_handled = is_http3 - || Curl_conn_is_http2(data, conn, FIRSTSOCKET); + data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET); if(!data_eof_handled && k->size != -1 && !k->header) { /* make sure we don't read too much */ @@ -472,7 +474,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, else { /* read nothing but since we wanted nothing we consider this an OK situation to proceed from */ - DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done"))); + DEBUGF(infof(data, "readwrite_data: we're done")); nread = 0; } @@ -490,15 +492,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(0 < nread || is_empty_data) { buf[nread] = 0; } - else { + if(!nread) { /* if we receive 0 or less here, either the data transfer is done or the server closed the connection and we bail out from this! */ if(data_eof_handled) DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); - k->keepon &= ~KEEP_RECV; - break; + k->keepon = 0; /* stop sending as well */ + if(!is_empty_data) + break; } /* Default buffer to use when we write the buffer, it may be changed @@ -666,6 +669,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, } k->bytecount += nread; + max_recv -= nread; Curl_pgrsSetDownloadCounter(data, k->bytecount); @@ -749,16 +753,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, break; } - } while(data_pending(data) && maxloops--); + } while((max_recv > 0) && data_pending(data) && maxloops--); - if(maxloops <= 0) { + if(maxloops <= 0 || max_recv <= 0) { /* we mark it as read-again-please */ data->state.dselect_bits = CURL_CSELECT_IN; *comeback = TRUE; } if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && - conn->bits.close) { + (conn->bits.close || data_eof_handled)) { /* When we've read the entire thing and the close bit is set, the server may now close the connection. If there's now any kind of sending going on from our side, we need to stop that immediately. */ @@ -768,7 +772,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, out: if(result) - DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result)); + DEBUGF(infof(data, "readwrite_data() -> %d", result)); return result; } @@ -821,9 +825,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, bool sending_http_headers = FALSE; struct SingleRequest *k = &data->req; - if((k->bytecount == 0) && (k->writebytecount == 0)) - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - *didwhat |= KEEP_SEND; do { @@ -1233,7 +1234,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE; out: if(result) - DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result)); + DEBUGF(infof(data, "Curl_readwrite() -> %d", result)); return result; } @@ -1332,7 +1333,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } data->state.prefer_ascii = data->set.prefer_ascii; +#ifdef CURL_LIST_ONLY_PROTOCOL data->state.list_only = data->set.list_only; +#endif data->state.httpreq = data->set.method; data->state.url = data->set.str[STRING_SET_URL]; @@ -1394,7 +1397,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) Curl_pgrsResetTransferSizes(data); Curl_pgrsStartNow(data); - /* In case the handle is re-used and an authentication method was picked + /* In case the handle is reused and an authentication method was picked in the session we need to make sure we only use the one(s) we now consider to be fine */ data->state.authhost.picked &= data->state.authhost.want; @@ -1551,10 +1554,11 @@ CURLcode Curl_follow(struct Curl_easy *data, if((type != FOLLOW_RETRY) && (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, 0, FALSE)) + Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { /* If this is not redirect due to a 401 or 407 response and an absolute URL: don't allow a custom port number */ disallowport = TRUE; + } DEBUGASSERT(data->state.uh); uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, @@ -1783,7 +1787,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif ) - /* We got no data, we attempted to re-use a connection. For HTTP this + /* We got no data, we attempted to reuse a connection. For HTTP this can be a retry so we try again regardless if we expected a body. For other protocols we only try again only if we expected a body. diff --git a/vendor/curl/lib/url.c b/vendor/curl/lib/url.c index 0fb6268341..4f5673ed0d 100644 --- a/vendor/curl/lib/url.c +++ b/vendor/curl/lib/url.c @@ -414,7 +414,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_hsts_cleanup(&data->hsts); curl_slist_free_all(data->set.hstslist); /* clean up list */ #endif -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif Curl_safefree(data->info.contenttype); @@ -457,6 +457,11 @@ CURLcode Curl_close(struct Curl_easy **datap) } #endif + Curl_mime_cleanpart(data->state.formp); +#ifndef CURL_DISABLE_HTTP + Curl_safefree(data->state.formp); +#endif + /* destruct wildcard structures if it is needed */ Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); @@ -490,7 +495,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->filesize = -1; /* we don't know the size */ set->postfieldsize = -1; /* unknown size */ - set->maxredirs = -1; /* allow any amount by default */ + set->maxredirs = 30; /* sensible default */ set->method = HTTPREQ_GET; /* Default HTTP request */ #ifndef CURL_DISABLE_RTSP @@ -659,6 +664,9 @@ CURLcode Curl_open(struct Curl_easy **curl) /* most recent connection is not yet defined */ data->state.lastconnect_id = -1; + data->state.recent_conn_id = -1; + /* and not assigned an id yet */ + data->id = -1; data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ @@ -680,7 +688,7 @@ CURLcode Curl_open(struct Curl_easy **curl) static void conn_shutdown(struct Curl_easy *data) { DEBUGASSERT(data); - infof(data, "Closing connection %ld", data->conn->connection_id); + infof(data, "Closing connection"); /* possible left-overs from the async name resolvers */ Curl_resolver_cancel(data); @@ -763,7 +771,8 @@ void Curl_disconnect(struct Curl_easy *data, /* the transfer must be detached from the connection */ DEBUGASSERT(!data->conn); - DEBUGF(infof(data, "Curl_disconnect(conn #%ld, dead=%d)", + DEBUGF(infof(data, "Curl_disconnect(conn #%" + CURL_FORMAT_CURL_OFF_T ", dead=%d)", conn->connection_id, dead_connection)); /* * If this connection isn't marked to force-close, leave it open if there @@ -937,22 +946,25 @@ static bool extract_if_dead(struct connectdata *conn, else { bool input_pending; + 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 * that we expect - in general - no waiting input data. Input * waiting might be a TLS Notify Close, for example. We reject * that. - * For protocols where data from other other end may arrive at + * For protocols where data from other end may arrive at * any time (HTTP/2 PING for example), the protocol handler needs * to install its own `connection_check` callback. */ dead = TRUE; } + Curl_detach_connection(data); } if(dead) { - infof(data, "Connection %ld seems to be dead", conn->connection_id); + infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead", + conn->connection_id); Curl_conncache_remove_conn(data, conn, FALSE); return TRUE; } @@ -1066,6 +1078,9 @@ ConnectionExists(struct Curl_easy *data, bool wantProxyNTLMhttp = FALSE; #endif #endif + /* plain HTTP with upgrade */ + bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && + (needle->handler->protocol & CURLPROTO_HTTP); *force_reuse = FALSE; *waitpipe = FALSE; @@ -1088,7 +1103,7 @@ ConnectionExists(struct Curl_easy *data, infof(data, "Server doesn't support multiplex yet, wait"); *waitpipe = TRUE; CONNCACHE_UNLOCK(data); - return FALSE; /* no re-use */ + return FALSE; /* no reuse */ } infof(data, "Server doesn't support multiplex (yet)"); @@ -1128,7 +1143,7 @@ ConnectionExists(struct Curl_easy *data, } if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { + && data->set.ipver != check->ip_version) { /* skip because the connection is not via the requested IP version */ continue; } @@ -1143,23 +1158,18 @@ ConnectionExists(struct Curl_easy *data, continue; } - if(Curl_resolver_asynch()) { - /* primary_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't re-use this connection */ - if(!check->primary_ip[0]) { - infof(data, - "Connection #%ld is still name resolving, can't reuse", - check->connection_id); - continue; - } - } + if(Curl_resolver_asynch() && + /* primary_ip[0] is NUL only if the resolving of the name hasn't + completed yet and until then we don't reuse this connection */ + !check->primary_ip[0]) + continue; } if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { foundPendingCandidate = TRUE; /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%ld isn't open enough, can't reuse", - check->connection_id); + infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T + "isn't open enough, can't reuse", check->connection_id); continue; } @@ -1231,6 +1241,17 @@ ConnectionExists(struct Curl_easy *data, } #endif + if(h2upgrade && !check->httpversion && canmultiplex) { + if(data->set.pipewait) { + infof(data, "Server upgrade doesn't support multiplex yet, wait"); + *waitpipe = TRUE; + CONNCACHE_UNLOCK(data); + return FALSE; /* no reuse */ + } + infof(data, "Server upgrade cannot be used"); + continue; /* can't be used atm */ + } + if(!canmultiplex && CONN_INUSE(check)) /* this request can't be multiplexed but the checked connection is already in use so we skip it */ @@ -1247,14 +1268,14 @@ ConnectionExists(struct Curl_easy *data, if(needle->localdev || needle->localport) { /* If we are bound to a specific local end (IP+port), we must not - re-use a random other one, although if we didn't ask for a + reuse a random other one, although if we didn't ask for a particular one we can reuse one that was bound. This comparison is a bit rough and too strict. Since the input parameters can be specified in numerous ways and still end up the same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that re-uses of bound connections - will most likely also re-use the exact same binding parameters and + Instead, this matching will assume that reuses of bound connections + will most likely also reuse the exact same binding parameters and missing out a few edge cases shouldn't hurt anyone very much. */ if((check->localport != needle->localport) || @@ -1287,7 +1308,7 @@ ConnectionExists(struct Curl_easy *data, (((check->httpversion >= 20) && (data->state.httpwant < CURL_HTTP_VERSION_2_0)) || ((check->httpversion >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) + (data->state.httpwant < CURL_HTTP_VERSION_3)))) continue; #ifdef USE_SSH else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { @@ -1335,8 +1356,8 @@ ConnectionExists(struct Curl_easy *data, if(!Curl_ssl_config_matches(&needle->ssl_config, &check->ssl_config)) { DEBUGF(infof(data, - "Connection #%ld has different SSL parameters, " - "can't reuse", + "Connection #%" CURL_FORMAT_CURL_OFF_T + " has different SSL parameters, can't reuse", check->connection_id)); continue; } @@ -1477,14 +1498,8 @@ void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn) { if(data->set.verbose) - infof(data, "Connected to %s (%s) port %u (#%ld)", -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname, - conn->primary_ip, conn->port, conn->connection_id); + infof(data, "Connected to %s (%s) port %u", + CURL_CONN_HOST_DISPNAME(conn), conn->primary_ip, conn->port); } #endif @@ -1580,8 +1595,10 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) if(!conn->localdev) goto error; } +#ifndef CURL_DISABLE_BINDLOCAL conn->localportrange = data->set.localportrange; conn->localport = data->set.localport; +#endif /* the close socket stuff needs to be copied to the connection struct as it may live on without (this specific) Curl_easy */ @@ -1857,7 +1874,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, * User name and password set with their own options override the * credentials possibly set in the URL. */ - if(!data->state.aptr.passwd) { + if(!data->set.str[STRING_PASSWORD]) { uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); if(!uc) { char *decoded; @@ -2113,7 +2130,7 @@ static char *detect_proxy(struct Curl_easy *data, /* * If this is supposed to use a proxy, we need to figure out the proxy - * host name, so that we can re-use an existing connection + * host name, so that we can reuse an existing connection * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, @@ -2434,7 +2451,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /*********************************************************************** * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can re-use an existing + * 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) { @@ -3252,7 +3269,7 @@ static CURLcode resolve_server(struct Curl_easy *data, /* Resolve the name of the server or proxy */ if(conn->bits.reuse) { /* We're reusing the connection - no need to resolve anything, and - idnconvert_hostname() was called already in create_conn() for the re-use + idnconvert_hostname() was called already in create_conn() for the reuse case. */ *async = FALSE; return CURLE_OK; @@ -3271,7 +3288,7 @@ static void reuse_conn(struct Curl_easy *data, struct connectdata *existing) { /* get the user+password information from the temp struct since it may - * be new for this request even when we re-use an existing connection */ + * be new for this request even when we reuse an existing connection */ if(temp->user) { /* use the new user name and password though */ Curl_safefree(existing->user); @@ -3331,14 +3348,14 @@ static void reuse_conn(struct Curl_easy *data, existing->hostname_resolve = temp->hostname_resolve; temp->hostname_resolve = NULL; - /* re-use init */ - existing->bits.reuse = TRUE; /* yes, we're re-using here */ + /* reuse init */ + existing->bits.reuse = TRUE; /* yes, we're reusing here */ conn_free(data, temp); } /** - * create_conn() sets up a new connectdata struct, or re-uses an already + * create_conn() sets up a new connectdata struct, or reuses an already * existing one, and resolves host name. * * if this function returns CURLE_OK and *async is set to TRUE, the resolve @@ -3650,7 +3667,7 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * Check the current list of connections to see if we can - * re-use an already existing one or if we have to create a + * reuse an already existing one or if we have to create a * new one. *************************************************************/ @@ -3658,7 +3675,7 @@ static CURLcode create_conn(struct Curl_easy *data, DEBUGASSERT(conn->passwd); /* reuse_fresh is TRUE if we are told to use a new connection by force, but - we only acknowledge this option if this is not a re-used connection + we only acknowledge this option if this is not a reused connection already (which happens due to follow-location or during an HTTP authentication phase). CONNECT_ONLY transfers also refuse reuse. */ if((data->set.reuse_fresh && !data->state.followlocation) || @@ -3678,15 +3695,14 @@ static CURLcode create_conn(struct Curl_easy *data, *in_connect = conn; #ifndef CURL_DISABLE_PROXY - infof(data, "Re-using existing connection #%ld with %s %s", - conn->connection_id, + infof(data, "Re-using existing connection with %s %s", 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); #else - infof(data, "Re-using existing connection #%ld with host %s", - conn->connection_id, conn->host.dispname); + infof(data, "Re-using existing connection with host %s", + conn->host.dispname); #endif } else { diff --git a/vendor/curl/lib/urlapi.c b/vendor/curl/lib/urlapi.c index a4530f919a..80299e7c0e 100644 --- a/vendor/curl/lib/urlapi.c +++ b/vendor/curl/lib/urlapi.c @@ -201,7 +201,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme) { - int i; + int i = 0; DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); (void)buflen; /* only used in debug-builds */ if(buf) @@ -210,17 +210,18 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) return 0; #endif - for(i = 0; i < MAX_SCHEME_LEN; ++i) { - char s = url[i]; - if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { - /* RFC 3986 3.1 explains: - scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ - } - else { - break; + if(ISALPHA(url[0])) + for(i = 1; i < MAX_SCHEME_LEN; ++i) { + char s = url[i]; + if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { + /* RFC 3986 3.1 explains: + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + } + else { + break; + } } - } if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) { /* If this does not guess scheme, the scheme always ends with the colon so that this also detects data: URLs etc. In guessing mode, data: could @@ -1108,6 +1109,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { /* This cannot be done with strcpy, as the memory chunks overlap! */ path++; + pathlen--; } #endif @@ -1383,6 +1385,7 @@ CURLU *curl_url_dup(const CURLU *in) DUP(u, in, path); DUP(u, in, query); DUP(u, in, fragment); + DUP(u, in, zoneid); u->portnum = in->portnum; } return u; @@ -1400,6 +1403,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, bool urldecode = (flags & CURLU_URLDECODE)?1:0; bool urlencode = (flags & CURLU_URLENCODE)?1:0; bool punycode = FALSE; + bool depunyfy = FALSE; bool plusdecode = FALSE; (void)flags; if(!u) @@ -1430,6 +1434,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ptr = u->host; ifmissing = CURLUE_NO_HOST; punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; break; case CURLUPART_ZONEID: ptr = u->zoneid; @@ -1480,6 +1485,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char *port = u->port; char *allochost = NULL; punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; if(u->scheme && strcasecompare("file", u->scheme)) { url = aprintf("file://%s%s%s", u->path, @@ -1539,14 +1545,28 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, #ifndef USE_IDN return CURLUE_LACKS_IDN; #else - allochost = Curl_idn_decode(u->host); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; + CURLcode result = Curl_idn_decode(u->host, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + CURLcode result = Curl_idn_encode(u->host, &allochost); + if(result) + /* this is the most likely error */ + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; #endif } } - url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s", scheme, u->user ? u->user : "", u->password ? ":": "", @@ -1557,7 +1577,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, allochost ? allochost : u->host, port ? ":": "", port ? port : "", - (u->path && (u->path[0] != '/')) ? "/": "", u->path ? u->path : "/", (u->query && u->query[0]) ? "?": "", (u->query && u->query[0]) ? u->query : "", @@ -1616,9 +1635,26 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, #ifndef USE_IDN return CURLUE_LACKS_IDN; #else - char *allochost = Curl_idn_decode(*part); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; + char *allochost; + CURLcode result = Curl_idn_decode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + free(*part); + *part = allochost; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + char *allochost; + CURLcode result = Curl_idn_encode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; free(*part); *part = allochost; #endif @@ -1639,8 +1675,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; bool plusencode = FALSE; bool urlskipslash = FALSE; + bool leadingslash = FALSE; bool appendquery = FALSE; bool equalsencode = FALSE; + size_t nalloc; if(!u) return CURLUE_BAD_HANDLE; @@ -1693,6 +1731,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OK; } + nalloc = strlen(part); + if(nalloc > CURL_MAX_INPUT_LENGTH) + /* excessive input length */ + return CURLUE_MALFORMED_INPUT; + switch(what) { case CURLUPART_SCHEME: { size_t plen = strlen(part); @@ -1706,13 +1749,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_UNSUPPORTED_SCHEME; storep = &u->scheme; urlencode = FALSE; /* never */ - /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ - while(plen--) { - if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) - s++; /* fine */ - else - return CURLUE_BAD_SCHEME; + if(ISALPHA(*s)) { + /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ + while(--plen) { + if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) + s++; /* fine */ + else + return CURLUE_BAD_SCHEME; + } } + else + return CURLUE_BAD_SCHEME; break; } case CURLUPART_USER: @@ -1746,6 +1793,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, break; case CURLUPART_PATH: urlskipslash = TRUE; + leadingslash = TRUE; /* enforce */ storep = &u->path; break; case CURLUPART_QUERY: @@ -1768,6 +1816,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, char *oldurl; char *redired_url; + if(!nalloc) + /* a blank URL is not a valid URL */ + return CURLUE_MALFORMED_INPUT; + /* if the new thing is absolute or the old one is not * (we could not get an absolute url in 'oldurl'), * then replace the existing with the new. */ @@ -1794,18 +1846,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } DEBUGASSERT(storep); { - const char *newp = part; - size_t nalloc = strlen(part); - - if(nalloc > CURL_MAX_INPUT_LENGTH) - /* excessive input length */ - return CURLUE_MALFORMED_INPUT; + const char *newp; + struct dynbuf enc; + Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash); + if(leadingslash && (part[0] != '/')) { + CURLcode result = Curl_dyn_addn(&enc, "/", 1); + if(result) + return CURLUE_OUT_OF_MEMORY; + } if(urlencode) { const unsigned char *i; - struct dynbuf enc; - - Curl_dyn_init(&enc, nalloc * 3 + 1); for(i = (const unsigned char *)part; *i; i++) { CURLcode result; @@ -1833,14 +1884,13 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OUT_OF_MEMORY; } } - newp = Curl_dyn_ptr(&enc); } else { char *p; - newp = strdup(part); - if(!newp) + CURLcode result = Curl_dyn_add(&enc, part); + if(result) return CURLUE_OUT_OF_MEMORY; - p = (char *)newp; + p = Curl_dyn_ptr(&enc); while(*p) { /* make sure percent encoded are lower case */ if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && @@ -1853,6 +1903,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, p++; } } + newp = Curl_dyn_ptr(&enc); if(appendquery) { /* Append the 'newp' string onto the old query. Add a '&' separator if @@ -1861,24 +1912,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, size_t querylen = u->query ? strlen(u->query) : 0; bool addamperand = querylen && (u->query[querylen -1] != '&'); if(querylen) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + struct dynbuf qbuf; + Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); - if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */ + if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */ goto nomem; if(addamperand) { - if(Curl_dyn_addn(&enc, "&", 1)) + if(Curl_dyn_addn(&qbuf, "&", 1)) goto nomem; } - if(Curl_dyn_add(&enc, newp)) + if(Curl_dyn_add(&qbuf, newp)) goto nomem; - free((char *)newp); + Curl_dyn_free(&enc); free(*storep); - *storep = Curl_dyn_ptr(&enc); + *storep = Curl_dyn_ptr(&qbuf); return CURLUE_OK; nomem: - free((char *)newp); + Curl_dyn_free(&enc); return CURLUE_OUT_OF_MEMORY; } } @@ -1890,7 +1941,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } else { if(!n || hostname_check(u, (char *)newp, n)) { - free((char *)newp); + Curl_dyn_free(&enc); return CURLUE_BAD_HOSTNAME; } } diff --git a/vendor/curl/lib/urldata.h b/vendor/curl/lib/urldata.h index f02e665414..4bfb3b48d2 100644 --- a/vendor/curl/lib/urldata.h +++ b/vendor/curl/lib/urldata.h @@ -101,6 +101,12 @@ typedef unsigned int curl_prot_t; #define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) #define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_POP3) +/* these protocols support CURLOPT_DIRLISTONLY */ +#define CURL_LIST_ONLY_PROTOCOL 1 +#endif + #define DEFAULT_CONNCACHE_SIZE 5 /* length of longest IPv6 address string including the trailing null */ @@ -330,6 +336,7 @@ struct Curl_ssl_session { #include "curl_sspi.h" #endif +#ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { #if defined(USE_WINDOWS_SSPI) @@ -353,6 +360,7 @@ struct digestdata { BIT(userhash); #endif }; +#endif typedef enum { NTLMSTATE_NONE, @@ -489,7 +497,7 @@ struct ConnectBits { #endif /* always modify bits.close with the connclose() and connkeep() macros! */ BIT(close); /* if set, we close the connection after this request */ - BIT(reuse); /* if set, this is a re-used connection */ + 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 */ @@ -629,17 +637,16 @@ struct SingleRequest { curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ - curl_off_t headerbytecount; /* only count received headers */ - curl_off_t deductheadercount; /* this amount of bytes doesn't count when we - check if anything has been transferred at - the end of a connection. We use this - counter to make only a 100 reply (without a - following second response code) result in a - CURLE_GOT_NOTHING error code */ - curl_off_t pendingheader; /* this many bytes left to send is actually header and not body */ struct curltime start; /* transfer started at this time */ + unsigned int headerbytecount; /* only count received headers */ + unsigned int deductheadercount; /* this amount of bytes doesn't count when + we check if anything has been transferred + at the end of a connection. We use this + counter to make only a 100 reply (without + a following second response code) result + in a CURLE_GOT_NOTHING error code */ enum { HEADER_NORMAL, /* no bad header at all */ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest @@ -701,7 +708,9 @@ struct SingleRequest { struct curltime last_sndbuf_update; /* last time readwrite_upload called win_update_buffer_size */ #endif +#ifndef CURL_DISABLE_COOKIES unsigned char setcookies; +#endif unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ BIT(content_range); /* set TRUE if Content-Range: was found */ @@ -747,7 +756,7 @@ struct Curl_handler { * after the connect() and everything is done, as a step in the connection. * The 'done' pointer points to a bool that should be set to TRUE if the * function completes before return. If it doesn't complete, the caller - * should call the curl_connecting() function until it is. + * should call the ->connecting() function until it is. */ CURLcode (*connect_it)(struct Curl_easy *data, bool *done); @@ -882,12 +891,12 @@ struct connectdata { #define CONN_INUSE(c) ((c)->easyq.size) /**** Fields set when inited and not modified again */ - long connection_id; /* Contains a unique number to make it easier to - track the connections in the log output */ + curl_off_t connection_id; /* Contains a unique number to make it easier to + track the connections in the log output */ /* 'dns_entry' is the particular host we use. This points to an entry in the DNS cache and it will not get pruned while locked. It gets unlocked in - multi_done(). This entry will be NULL if the connection is re-used as then + multi_done(). This entry will be NULL if the connection is reused as then there is no name resolve done. */ struct Curl_dns_entry *dns_entry; @@ -1024,14 +1033,19 @@ struct connectdata { #ifndef CURL_DISABLE_SMB struct smb_conn smbc; #endif +#ifdef USE_LIBRTMP void *rtmp; +#endif +#ifdef USE_OPENLDAP struct ldapconninfo *ldapc; +#endif #ifndef CURL_DISABLE_MQTT struct mqtt_conn mqtt; #endif #ifdef USE_WEBSOCKETS struct websocket *ws; #endif + unsigned int unused:1; /* avoids empty union */ } proto; struct connectbundle *bundle; /* The bundle we are member of */ @@ -1045,7 +1059,7 @@ struct connectdata { /* 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 - that subsequent bound-requested connections aren't accidentally re-using + that subsequent bound-requested connections aren't accidentally reusing wrong connections. */ char *localdev; unsigned short localportrange; @@ -1077,6 +1091,18 @@ struct connectdata { unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; +#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) +#else +#define CURL_CONN_HOST_DISPNAME(c) \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname +#endif + /* The end of connectdata. */ /* @@ -1089,7 +1115,6 @@ struct PureInfo { int httpversion; /* the http version number X.Y = X*10+Y */ time_t filetime; /* If requested, this is might get set. Set to -1 if the time was unretrievable. */ - curl_off_t header_size; /* size of read header(s) in bytes */ curl_off_t request_size; /* the amount of bytes sent in the request(s) */ unsigned long proxyauthavail; /* what proxy auth types were announced */ unsigned long httpauthavail; /* what host auth types were announced */ @@ -1097,6 +1122,7 @@ struct PureInfo { char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would've been redirected to if asked to */ curl_off_t retry_after; /* info from Retry-After: header */ + unsigned int header_size; /* size of read header(s) in bytes */ /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' and, 'conn_local_port' are copied over from the connectdata struct in @@ -1116,10 +1142,8 @@ struct PureInfo { int conn_local_port; const char *conn_scheme; unsigned int conn_protocol; - struct curl_certinfo certs; /* info about the certs, only populated in - OpenSSL, GnuTLS, Schannel, NSS and GSKit - builds. Asked for with CURLOPT_CERTINFO - / CURLINFO_CERTINFO */ + struct curl_certinfo certs; /* info about the certs. Asked for with + CURLOPT_CERTINFO / CURLINFO_CERTINFO */ CURLproxycode pxcode; BIT(timecond); /* set to TRUE if the time condition didn't match, which thus made the document NOT get fetched */ @@ -1294,7 +1318,9 @@ struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ - long lastconnect_id; /* The last connection, -1 if undefined */ + curl_off_t lastconnect_id; /* The last connection, -1 if undefined */ + curl_off_t recent_conn_id; /* The most recent connection used, might no + * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ char *buffer; /* download buffer */ @@ -1323,7 +1349,7 @@ struct UrlState { /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ void (*prev_signal)(int sig); #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif @@ -1381,6 +1407,9 @@ struct UrlState { struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP + curl_mimepart *mimepost; + curl_mimepart *formp; /* storage for old API form-posting, alloced on + demand */ size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ @@ -1452,9 +1481,13 @@ struct UrlState { when multi_done() is called, to prevent multi_done() to get invoked twice when the multi interface is used. */ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ +#ifndef CURL_DISABLE_COOKIES BIT(cookie_engine); +#endif BIT(prefer_ascii); /* ASCII rather than binary */ +#ifdef CURL_LIST_ONLY_PROTOCOL BIT(list_only); /* list directory contents */ +#endif BIT(url_alloc); /* URL string is malloc()'ed */ BIT(referer_alloc); /* referer string is malloc()ed */ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ @@ -1563,6 +1596,7 @@ enum dupstring { STRING_DNS_LOCAL_IP6, STRING_SSL_EC_CURVES, STRING_AWS_SIGV4, /* Parameters for V4 signature */ + STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ /* -- end of null-terminated strings -- */ @@ -1610,10 +1644,12 @@ struct UserDefined { curl_off_t postfieldsize; /* if POST, this might have a size to use instead of strlen(), and then the data *may* be binary (contain zero bytes) */ +#ifndef CURL_DISABLE_BINDLOCAL unsigned short localport; /* local port number to bind to */ unsigned short localportrange; /* number of additional port numbers to test in case the 'localport' one can't be bind()ed */ +#endif curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ @@ -1801,7 +1837,9 @@ struct UserDefined { BIT(tftp_no_options); /* do not send TFTP options requests */ #endif BIT(sep_headers); /* handle host and proxy headers separately */ +#ifndef CURL_DISABLE_COOKIES BIT(cookiesession); /* new cookie session? */ +#endif BIT(crlf); /* convert crlf on ftp upload(?) */ BIT(ssh_compression); /* enable SSH compression */ @@ -1816,7 +1854,9 @@ struct UserDefined { BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */ BIT(prefer_ascii); /* ASCII rather than binary */ BIT(remote_append); /* append, not overwrite, on upload */ +#ifdef CURL_LIST_ONLY_PROTOCOL BIT(list_only); /* list directory */ +#endif #ifndef CURL_DISABLE_FTP BIT(ftp_use_port); /* use the FTP PORT command */ BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */ @@ -1840,7 +1880,7 @@ struct UserDefined { BIT(verbose); /* output verbosity */ BIT(krb); /* Kerberos connection requested */ BIT(reuse_forbid); /* forbidden to be reused, close after use */ - BIT(reuse_fresh); /* do not re-use an existing connection */ + BIT(reuse_fresh); /* do not reuse an existing connection */ BIT(no_signal); /* do not use any signal/alarm handler */ BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ BIT(ignorecl); /* ignore content length */ @@ -1902,6 +1942,13 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ unsigned int magic; + /* once an easy handle is tied to a connection cache + a non-negative number to distinguish this transfer from + other using the same cache. For easier tracking + in log output. + This may wrap around after LONG_MAX to 0 again, so it + has no uniqueness guarantuee for very large processings. */ + curl_off_t id; /* first, two fields for the linked list of these */ struct Curl_easy *next; diff --git a/vendor/curl/lib/vauth/cram.c b/vendor/curl/lib/vauth/cram.c index 5894ed4bcf..91fb261c57 100644 --- a/vendor/curl/lib/vauth/cram.c +++ b/vendor/curl/lib/vauth/cram.c @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #include #include "urldata.h" @@ -94,4 +94,4 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, return CURLE_OK; } -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/vendor/curl/lib/vauth/digest.c b/vendor/curl/lib/vauth/digest.c index fda2d911f7..12c6f7dd5b 100644 --- a/vendor/curl/lib/vauth/digest.c +++ b/vendor/curl/lib/vauth/digest.c @@ -27,7 +27,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #include @@ -420,7 +420,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ - spn = Curl_auth_build_spn(service, realm, NULL); + spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); if(!spn) return CURLE_OUT_OF_MEMORY; @@ -992,4 +992,4 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } #endif /* !USE_WINDOWS_SSPI */ -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/vendor/curl/lib/vauth/digest.h b/vendor/curl/lib/vauth/digest.h index 68fdb28c47..99ce1f9138 100644 --- a/vendor/curl/lib/vauth/digest.h +++ b/vendor/curl/lib/vauth/digest.h @@ -26,7 +26,7 @@ #include -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #define DIGEST_MAX_VALUE_LENGTH 256 #define DIGEST_MAX_CONTENT_LENGTH 1024 diff --git a/vendor/curl/lib/vauth/digest_sspi.c b/vendor/curl/lib/vauth/digest_sspi.c index 8fb8669393..02e36ea5ed 100644 --- a/vendor/curl/lib/vauth/digest_sspi.c +++ b/vendor/curl/lib/vauth/digest_sspi.c @@ -27,7 +27,7 @@ #include "curl_setup.h" -#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) #include @@ -665,4 +665,4 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) Curl_safefree(digest->passwd); } -#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/vendor/curl/lib/vauth/ntlm.c b/vendor/curl/lib/vauth/ntlm.c index 93096ba5ec..ed7cee8def 100644 --- a/vendor/curl/lib/vauth/ntlm.c +++ b/vendor/curl/lib/vauth/ntlm.c @@ -45,12 +45,6 @@ #include "rand.h" #include "vtls/vtls.h" -/* SSL backend-specific #if branches in this file must be kept in the order - documented in curl_ntlm_core. */ -#if defined(NTLM_NEEDS_NSS_INIT) -#include "vtls/nssg.h" /* for Curl_nss_force_init() */ -#endif - #define BUILDING_CURL_NTLM_MSGS_C #include "vauth/vauth.h" #include "vauth/ntlm.h" @@ -274,12 +268,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, const unsigned char *type2 = Curl_bufref_ptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#if defined(NTLM_NEEDS_NSS_INIT) - /* Make sure the crypto backend is initialized */ - result = Curl_nss_force_init(data); - if(result) - return result; -#elif defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(CURL_DISABLE_VERBOSE_STRINGS) (void)data; #endif diff --git a/vendor/curl/lib/vauth/vauth.h b/vendor/curl/lib/vauth/vauth.h index d8cff24381..9da0540892 100644 --- a/vendor/curl/lib/vauth/vauth.h +++ b/vendor/curl/lib/vauth/vauth.h @@ -30,7 +30,7 @@ struct Curl_easy; -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_DIGEST_AUTH) struct digestdata; #endif @@ -86,7 +86,7 @@ CURLcode Curl_auth_create_login_message(const char *value, CURLcode Curl_auth_create_external_message(const char *user, struct bufref *out); -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#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, @@ -119,7 +119,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* This is used to clean up the digest specific data */ void Curl_auth_digest_cleanup(struct digestdata *digest); -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ #ifdef USE_GSASL /* This is used to evaluate if MECH is supported by gsasl */ diff --git a/vendor/curl/lib/version.c b/vendor/curl/lib/version.c index c036e97178..47304259e0 100644 --- a/vendor/curl/lib/version.c +++ b/vendor/curl/lib/version.c @@ -300,7 +300,7 @@ char *curl_version(void) protocol line has its own #if line to make things easier on the eye. */ -static const char * const protocols[] = { +static const char * const supported_protocols[] = { #ifndef CURL_DISABLE_DICT "dict", #endif @@ -535,7 +535,7 @@ static curl_version_info_data version_info = { NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ NULL, /* zlib_version */ - protocols, + supported_protocols, NULL, /* c-ares version */ 0, /* c-ares version numerical */ NULL, /* libidn version */ diff --git a/vendor/curl/lib/vquic/curl_msh3.c b/vendor/curl/lib/vquic/curl_msh3.c index 173886739b..6bd0d23316 100644 --- a/vendor/curl/lib/vquic/curl_msh3.c +++ b/vendor/curl/lib/vquic/curl_msh3.c @@ -30,7 +30,7 @@ #include "timeval.h" #include "multiif.h" #include "sendf.h" -#include "curl_log.h" +#include "curl_trc.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" @@ -123,6 +123,7 @@ struct cf_msh3_ctx { }; /* How to access `call_data` from a cf_msh3 filter */ +#undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) \ ((struct cf_msh3_ctx *)(cf)->ctx)->call_data @@ -172,7 +173,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, msh3_lock_initialize(&stream->recv_lock); Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data)); + CURL_TRC_CF(data, cf, "data setup"); return CURLE_OK; } @@ -182,7 +183,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "easy handle is done")); + CURL_TRC_CF(data, cf, "easy handle is done"); Curl_bufq_free(&stream->recvbuf); free(stream); H3_STREAM_LCTX(data) = NULL; @@ -234,7 +235,7 @@ static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)Connection; - DEBUGF(LOG_CF(data, cf, "[MSH3] connected")); + CURL_TRC_CF(data, cf, "[MSH3] connected"); ctx->handshake_succeeded = true; ctx->connected = true; ctx->handshake_complete = true; @@ -248,7 +249,7 @@ static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)Connection; - DEBUGF(LOG_CF(data, cf, "[MSH3] shutdown complete")); + CURL_TRC_CF(data, cf, "[MSH3] shutdown complete"); ctx->connected = false; ctx->handshake_complete = true; } @@ -473,18 +474,18 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) { failf(data, "HTTP/3 stream reset by server"); *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "cf_recv, was reset -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err); goto out; } else if(stream->error3) { failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", (ssize_t)stream->error3); *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err); goto out; } else { - DEBUGF(LOG_CF(data, cf, "cf_recv, closed ok -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err); } *err = CURLE_OK; nread = 0; @@ -522,7 +523,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1; } CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len)); + CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len); msh3_lock_acquire(&stream->recv_lock); @@ -537,8 +538,8 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", - len, nread, *err)); + CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", + len, nread, *err); if(nread < 0) goto out; if(stream->closed) @@ -549,7 +550,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } else { - DEBUGF(LOG_CF(data, cf, "req: nothing here, call again")); + CURL_TRC_CF(data, cf, "req: nothing here, call again"); *err = CURLE_AGAIN; } @@ -580,7 +581,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* Sizes must match for cast below to work" */ DEBUGASSERT(stream); - DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len)); + CURL_TRC_CF(data, cf, "req: send %zu bytes", len); if(!stream->req) { /* The first send on the request contains the headers and possibly some @@ -629,7 +630,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, break; } - DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader)); + CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, nva, nheader, eos ? MSH3_REQUEST_FLAG_FIN : @@ -645,7 +646,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { /* request is open */ - DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len)); + CURL_TRC_CF(data, cf, "req: send %zu body bytes", len); if(len > 0xFFFFFFFF) { len = 0xFFFFFFFF; } @@ -693,7 +694,7 @@ static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, drain_stream(cf, data); } } - DEBUGF(LOG_CF(data, cf, "select_sock -> %d", bitmap)); + CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap); CF_DATA_RESTORE(cf, save); return bitmap; } @@ -710,8 +711,8 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf, (void)cf; if(stream && stream->req) { msh3_lock_acquire(&stream->recv_lock); - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu", - Curl_bufq_len(&stream->recvbuf))); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu", + Curl_bufq_len(&stream->recvbuf)); pending = !Curl_bufq_is_empty(&stream->recvbuf); msh3_lock_release(&stream->recv_lock); if(pending) @@ -773,7 +774,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, h3_data_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: - DEBUGF(LOG_CF(data, cf, "req: send done")); + CURL_TRC_CF(data, cf, "req: send done"); if(stream) { stream->upload_done = TRUE; if(stream->req) { @@ -786,7 +787,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, } break; case CF_CTRL_CONN_INFO_UPDATE: - DEBUGF(LOG_CF(data, cf, "req: update info")); + CURL_TRC_CF(data, cf, "req: update info"); cf_msh3_active(cf, data); break; default: @@ -812,17 +813,17 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, /* TODO: need a way to provide trust anchors to MSH3 */ #ifdef DEBUGBUILD /* we need this for our test cases to run */ - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "switching off verifypeer in DEBUG mode")); + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "switching off verifypeer in DEBUG mode"); verify = 0; #else - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "attempting with built-in verification")); + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "attempting with built-in verification"); #endif } - DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)", - cf->conn->host.name, (int)cf->conn->remote_port, verify)); + CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)", + cf->conn->host.name, (int)cf->conn->remote_port, verify); ctx->api = MsH3ApiOpen(); if(!ctx->api) { @@ -887,7 +888,7 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, if(ctx->handshake_complete) { ctx->handshake_at = Curl_now(); if(ctx->handshake_succeeded) { - DEBUGF(LOG_CF(data, cf, "handshake succeeded")); + CURL_TRC_CF(data, cf, "handshake succeeded"); cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; @@ -917,7 +918,7 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx) { - DEBUGF(LOG_CF(data, cf, "destroying")); + CURL_TRC_CF(data, cf, "destroying"); if(ctx->qconn) { MsH3ConnectionClose(ctx->qconn); ctx->qconn = NULL; @@ -934,13 +935,13 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) */ ctx->active = FALSE; if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active", - (int)ctx->sock[SP_LOCAL])); + CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active", + (int)ctx->sock[SP_LOCAL]); cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; } else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at " - "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL])); + CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at " + "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; } if(cf->sockindex == FIRSTSOCKET) diff --git a/vendor/curl/lib/vquic/curl_ngtcp2.c b/vendor/curl/lib/vquic/curl_ngtcp2.c index 7627940ff5..03e911d184 100644 --- a/vendor/curl/lib/vquic/curl_ngtcp2.c +++ b/vendor/curl/lib/vquic/curl_ngtcp2.c @@ -33,7 +33,7 @@ #ifdef OPENSSL_IS_BORINGSSL #include #else -#include +#include #endif #include "vtls/openssl.h" #elif defined(USE_GNUTLS) @@ -58,6 +58,7 @@ #include "dynbuf.h" #include "http1.h" #include "select.h" +#include "inet_pton.h" #include "vquic.h" #include "vquic_int.h" #include "vtls/keylog.h" @@ -162,20 +163,26 @@ struct cf_ngtcp2_ctx { size_t max_stream_window; /* max flow window for one stream */ int qlogfd; BIT(got_first_byte); /* if first byte was received */ +#ifdef USE_OPENSSL + BIT(x509_store_setup); /* if x509 store has been set up */ +#endif }; /* 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 /** * All about the H3 internals of a stream */ -struct stream_ctx { +struct h3_stream_ctx { int64_t id; /* HTTP/3 protocol identifier */ struct bufq sendbuf; /* h3 request body */ struct bufq recvbuf; /* h3 response body */ + struct h1_req_parser h1; /* h1 request parsing */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + size_t upload_blocked_len; /* the amount written last and EGAINed */ size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ @@ -186,18 +193,18 @@ struct stream_ctx { bool send_closed; /* stream is local closed */ }; -#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) +#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ + ((struct HTTP *)(d)->req.p.http)->h3_ctx \ + : NULL)) +#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx +#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ + H3_STREAM_CTX(d)->id : -2) static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); if(!data || !data->req.p.http) { failf(data, "initialization failure, transfer not http initialized"); @@ -221,22 +228,22 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); stream->recv_buf_nonflow = 0; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); H3_STREAM_LCTX(data) = stream; - DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data)); return CURLE_OK; } static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); free(stream); H3_STREAM_LCTX(data) = NULL; } @@ -246,10 +253,37 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) the maximum packet burst to MAX_PKT_BURST packets. */ #define MAX_PKT_BURST 10 -static CURLcode cf_process_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data); -static CURLcode cf_flush_egress(struct Curl_cfilter *cf, - struct Curl_easy *data); +struct pkt_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + size_t pkt_count; + ngtcp2_path_storage ps; +}; + +static ngtcp2_tstamp timestamp(void) +{ + struct curltime ct = Curl_now(); + return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; +} + +static void pktx_init(struct pkt_io_ctx *pktx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + pktx->cf = cf; + pktx->data = data; + pktx->ts = timestamp(); + pktx->pkt_count = 0; + ngtcp2_path_storage_zero(&pktx->ps); +} + +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); static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, uint64_t datalen, void *user_data, void *stream_user_data); @@ -261,12 +295,6 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) return ctx->qconn; } -static ngtcp2_tstamp timestamp(void) -{ - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; -} - #ifdef DEBUG_NGTCP2 static void quic_printf(void *user_data, const char *fmt, ...) { @@ -300,7 +328,8 @@ static void qlog_callback(void *user_data, uint32_t flags, } static void quic_settings(struct cf_ngtcp2_ctx *ctx, - struct Curl_easy *data) + struct Curl_easy *data, + struct pkt_io_ctx *pktx) { ngtcp2_settings *s = &ctx->settings; ngtcp2_transport_params *t = &ctx->transport_params; @@ -314,7 +343,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, #endif (void)data; - s->initial_ts = timestamp(); + s->initial_ts = pktx->ts; s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; s->max_window = 100 * ctx->max_stream_window; s->max_stream_window = ctx->max_stream_window; @@ -327,7 +356,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, t->initial_max_streams_uni = QUIC_MAX_STREAMS; t->max_idle_timeout = QUIC_IDLE_TIMEOUT; if(ctx->qlogfd != -1) { - s->qlog.write = qlog_callback; + s->qlog_write = qlog_callback; } } @@ -368,6 +397,7 @@ static int init_ngh3_conn(struct Curl_cfilter *cf); static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct connectdata *conn = cf->conn; CURLcode result = CURLE_FAILED_INIT; SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); @@ -383,8 +413,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, goto out; } #else - if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_openssl_configure_client_context failed"); + if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); goto out; } #endif @@ -416,10 +446,6 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); } - result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); - if(result) - goto out; - /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with @@ -429,6 +455,15 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + /* When a user callback is installed to modify the SSL_CTX, + * we need to do the full initialization before calling it. + * See: #11800 */ + if(!ctx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); + if(result) + goto out; + ctx->x509_store_setup = TRUE; + } Curl_set_in_callback(data, true); result = (*data->set.ssl.fsslctx)(data, ssl_ctx, data->set.ssl.fsslctxp); @@ -477,8 +512,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; const uint8_t *alpn = NULL; size_t alpnlen = 0; + unsigned char checkip[16]; - (void)data; DEBUGASSERT(!ctx->ssl); ctx->ssl = SSL_new(ctx->sslctx); @@ -492,7 +527,19 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); /* set SNI */ - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) +#ifdef ENABLE_IPV6 + && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) +#endif + ) { + char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); + if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + failf(data, "Failed set SNI"); + SSL_free(ctx->ssl); + ctx->ssl = NULL; + return CURLE_QUIC_CONNECT_ERROR; + } + } return CURLE_OK; } #elif defined(USE_GNUTLS) @@ -520,15 +567,15 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref); if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { - DEBUGF(LOG_CF(data, cf, - "ngtcp2_crypto_gnutls_configure_client_session failed\n")); + CURL_TRC_CF(data, cf, + "ngtcp2_crypto_gnutls_configure_client_session failed\n"); return CURLE_QUIC_CONNECT_ERROR; } rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); if(rc < 0) { - DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc))); + CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", + gnutls_strerror(rc)); return CURLE_QUIC_CONNECT_ERROR; } @@ -572,7 +619,7 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { char error_buffer[256]; ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + failf(data, "wolfSSL_CTX_set_cipher_list: %s", error_buffer); goto out; } @@ -686,7 +733,7 @@ static void report_consumed_data(struct Curl_cfilter *cf, struct Curl_easy *data, size_t consumed) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); struct cf_ngtcp2_ctx *ctx = cf->ctx; if(!stream) @@ -704,8 +751,8 @@ static void report_consumed_data(struct Curl_cfilter *cf, } } if(consumed > 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes", - stream->id, consumed)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA", + stream->id, consumed); ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, consumed); ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); @@ -727,8 +774,8 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, nconsumed = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed); if(nconsumed < 0) { ngtcp2_ccerr_set_application_error( &ctx->last_error, @@ -786,8 +833,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%" - PRIu64 ") -> %d", stream3_id, app_error_code, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" + PRIu64 ") -> %d", stream3_id, app_error_code, rv); if(rv) { ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); @@ -811,7 +858,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -902,13 +949,13 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, return 0; } -static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level, +static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, void *user_data) { struct Curl_cfilter *cf = user_data; (void)tconn; - if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) { + if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { return 0; } @@ -962,6 +1009,61 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* early_data_rejected */ }; +/** + * 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(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; + ngtcp2_tstamp expiry; + + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx->ts = timestamp(); + } + + 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)); + ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); + 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, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); + } + } + return CURLE_OK; +} + static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) @@ -969,7 +1071,7 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct SingleRequest *k = &data->req; int rv = GETSOCK_BLANK; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); struct cf_call_data save; CF_DATA_SAVE(save, cf, data); @@ -985,21 +1087,19 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) rv |= GETSOCK_WRITESOCK(0); - /* DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)", - rv, (int)socks[0])); */ CF_DATA_RESTORE(cf, save); return rv; } -static void drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void h3_drain_stream(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); unsigned char bits; (void)cf; bits = CURL_CSELECT_IN; - if(stream && !stream->send_closed && stream->upload_left) + if(stream && stream->upload_left && !stream->send_closed) bits |= CURL_CSELECT_OUT; if(data->state.dselect_bits != bits) { data->state.dselect_bits = bits; @@ -1013,25 +1113,27 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)conn; (void)stream_id; - (void)app_error_code; - (void)cf; /* we might be called by nghttp3 after we already cleaned up */ if(!stream) return 0; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")", - stream_id, app_error_code)); stream->closed = TRUE; stream->error3 = app_error_code; - if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) { + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, + stream->id, stream->error3); } - drain_stream(cf, data); + else { + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + } + data->req.keepon &= ~KEEP_SEND_HOLD; + h3_drain_stream(cf, data); return 0; } @@ -1045,7 +1147,7 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, const void *mem, size_t memlen, bool flow) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); CURLcode result = CURLE_OK; ssize_t nwritten; @@ -1054,9 +1156,6 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; } nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) " - "-> %zd, %d", stream->id, memlen, nwritten, result)); - */ if(nwritten < 0) { return result; } @@ -1079,14 +1178,24 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); CURLcode result; (void)conn; (void)stream3_id; + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + result = write_resp_raw(cf, data, buf, buflen, TRUE); - drain_stream(cf, data); - return result? -1 : 0; + if(result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", + stream->id, buflen, result); + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen); + h3_drain_stream(cf, data); + return 0; } static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, @@ -1110,7 +1219,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); CURLcode result = CURLE_OK; (void)conn; (void)stream_id; @@ -1125,12 +1234,12 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, return -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d", - stream_id, stream->status_code)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; } - drain_stream(cf, data); + h3_drain_stream(cf, data); return 0; } @@ -1143,7 +1252,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); struct Curl_easy *data = stream_user_data; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); CURLcode result = CURLE_OK; (void)conn; (void)stream_id; @@ -1165,8 +1274,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, return -1; ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", stream->status_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s", - stream_id, line)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); result = write_resp_raw(cf, data, line, ncopy, FALSE); if(result) { return -1; @@ -1174,9 +1282,9 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, } else { /* store as an HTTP1-style header */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" 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 = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); if(result) { return -1; @@ -1207,7 +1315,8 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, (void)conn; (void)stream_user_data; - rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code); + rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1225,9 +1334,9 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, (void)conn; (void)data; - rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id, + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1249,7 +1358,8 @@ static nghttp3_callbacks ngh3_callbacks = { cb_h3_stop_sending, NULL, /* end_stream */ cb_h3_reset_stream, - NULL /* shutdown */ + NULL, /* shutdown */ + NULL /* recv_settings */ }; static int init_ngh3_conn(struct Curl_cfilter *cf) @@ -1314,7 +1424,7 @@ static int init_ngh3_conn(struct Curl_cfilter *cf) static ssize_t recv_closed_stream(struct Curl_cfilter *cf, struct Curl_easy *data, - struct stream_ctx *stream, + struct h3_stream_ctx *stream, CURLcode *err) { ssize_t nread = -1; @@ -1323,35 +1433,17 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) { failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); - goto out; - } - else if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - failf(data, - "HTTP/3 stream %" PRId64 " was not closed cleanly: " - "(err %"PRId64")", stream->id, stream->error3); - *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly" - " -> %d", stream->id, *err)); + *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } - - if(!stream->resp_hds_complete) { + 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; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); goto out; } - else { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); - } *err = CURLE_OK; nread = 0; @@ -1364,9 +1456,10 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); ssize_t nread = -1; struct cf_call_data save; + struct pkt_io_ctx pktx; (void)ctx; @@ -1377,6 +1470,8 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(ctx->h3conn); *err = CURLE_OK; + pktx_init(&pktx, cf, data); + if(!stream) { *err = CURLE_RECV_ERROR; goto out; @@ -1385,14 +1480,15 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); goto out; + } report_consumed_data(cf, data, nread); } - if(cf_process_ingress(cf, data)) { + if(cf_progress_ingress(cf, data, &pktx)) { *err = CURLE_RECV_ERROR; nread = -1; goto out; @@ -1402,15 +1498,16 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); goto out; + } report_consumed_data(cf, data, nread); } if(nread > 0) { - drain_stream(cf, data); + h3_drain_stream(cf, data); } else { if(stream->closed) { @@ -1422,12 +1519,19 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - if(cf_flush_egress(cf, data)) { + if(cf_progress_egress(cf, data, &pktx)) { *err = CURLE_SEND_ERROR; nread = -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nread, *err)); + else { + CURLcode result2 = check_and_set_expiry(cf, data, &pktx); + if(result2) { + *err = result2; + nread = -1; + } + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1438,7 +1542,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); size_t skiplen; (void)cf; @@ -1454,10 +1558,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, Curl_bufq_skip(&stream->sendbuf, skiplen); stream->sendbuf_len_in_flight -= skiplen; - /* `sendbuf` *might* now have more room. If so, resume this - * possibly paused stream. And also tell our transfer engine that - * it may continue KEEP_SEND if told to PAUSE. */ - if(!Curl_bufq_is_full(&stream->sendbuf)) { + /* Everything ACKed, we resume upload processing */ + if(!stream->sendbuf_len_in_flight) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -1465,9 +1567,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, if((data->req.keepon & KEEP_SEND_HOLD) && (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; - drain_stream(cf, data); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks", - stream_id)); + h3_drain_stream(cf, data); + CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id); } } return 0; @@ -1481,7 +1582,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); ssize_t nwritten = 0; size_t nvecs = 0; (void)cf; @@ -1524,16 +1625,18 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> AGAIN", - stream->id)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%zd)", stream->id, - (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" + CURL_FORMAT_CURL_OFF_T ")", + stream->id, (int)nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); return (nghttp3_ssize)nvecs; } @@ -1547,8 +1650,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = NULL; - struct h1_req_parser h1; + struct h3_stream_ctx *stream = NULL; struct dynhds h2_headers; size_t nheader; nghttp3_nv *nva = NULL; @@ -1558,7 +1660,6 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nghttp3_data_reader reader; nghttp3_data_reader *preader = NULL; - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = h3_data_setup(cf, data); @@ -1567,24 +1668,22 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, stream = H3_STREAM_CTX(data); DEBUGASSERT(stream); - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); - if(rc) { - failf(data, "can get bidi streams"); - *err = CURLE_SEND_ERROR; - goto out; - } - - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(nghttp3_nv) * nheader); @@ -1603,6 +1702,13 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nva[i].flags = NGHTTP3_NV_FLAG_NONE; } + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); + if(rc) { + failf(data, "can get bidi streams"); + *err = CURLE_SEND_ERROR; + goto out; + } + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -1614,27 +1720,30 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, else /* data sending without specifying the data amount up front */ stream->upload_left = -1; /* unknown */ - reader.read_data = cb_h3_read_req_body; - preader = &reader; break; default: /* there is not request body */ stream->upload_left = 0; /* no request body */ - preader = NULL; break; } + stream->send_closed = (stream->upload_left == 0); + if(!stream->send_closed) { + reader.read_data = cb_h3_read_req_body; + preader = &reader; + } + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, nva, nheader, preader, data); if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, " - "connection is closing", stream->id)); + CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " + "connection is closing", stream->id); break; default: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", - stream->id, rc, ngtcp2_strerror(rc))); + CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", + stream->id, rc, ngtcp2_strerror(rc)); break; } *err = CURLE_SEND_ERROR; @@ -1642,14 +1751,18 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, goto out; } - infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)", - stream->id, (void *)data); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream->id, data->state.url)); + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } out: free(nva); - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -1658,55 +1771,105 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); ssize_t sent = 0; struct cf_call_data save; + struct pkt_io_ctx pktx; + CURLcode result; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->qconn); DEBUGASSERT(ctx->h3conn); + pktx_init(&pktx, cf, data); *err = CURLE_OK; - if(stream && stream->closed) { - *err = CURLE_HTTP3; + result = cf_progress_ingress(cf, data, &pktx); + if(result) { + *err = result; sent = -1; - goto out; } if(!stream || stream->id < 0) { sent = h3_stream_open(cf, data, buf, len, err); if(sent < 0) { - DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err)); + CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); + goto out; + } + stream = H3_STREAM_CTX(data); + } + else if(stream->upload_blocked_len) { + /* the data in `buf` has already been submitted or added to the + * buffers, but have been EAGAINed on the last invocation. */ + DEBUGASSERT(len >= stream->upload_blocked_len); + if(len < stream->upload_blocked_len) { + /* Did we get called again with a smaller `len`? This should not + * happen. We are not prepared to handle that. */ + failf(data, "HTTP/3 send again with decreased length"); + *err = CURLE_HTTP3; + sent = -1; + goto out; + } + sent = (ssize_t)stream->upload_blocked_len; + stream->upload_blocked_len = 0; + } + else 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); + *err = CURLE_OK; + sent = (ssize_t)len; goto out; } + *err = CURLE_HTTP3; + sent = -1; + goto out; } else { sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %zd, %d", - stream->id, len, sent, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %zd, %d", + stream->id, len, sent, *err); if(sent < 0) { - if(*err == CURLE_AGAIN) { - /* Can't add more to the send buf, needs to drain first. - * Pause the sending to avoid a busy loop. */ - data->req.keepon |= KEEP_SEND_HOLD; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] pause send", - stream->id)); - } goto out; } (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } - if(cf_flush_egress(cf, data)) { - *err = CURLE_SEND_ERROR; + result = cf_progress_egress(cf, data, &pktx); + if(result) { + *err = result; sent = -1; - goto out; + } + + if(stream && sent > 0 && stream->sendbuf_len_in_flight) { + /* We have unacknowledged DATA and cannot report success to our + * caller. Instead we EAGAIN and remember how much we have already + * "written" into our various internal connection buffers. + * We put the stream upload on HOLD, until this gets ACKed. */ + stream->upload_blocked_len = sent; + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " + "%zu bytes in flight -> EGAIN", stream->id, len, + stream->sendbuf_len_in_flight); + *err = CURLE_AGAIN; + sent = -1; + data->req.keepon |= KEEP_SEND_HOLD; } out: + result = check_and_set_expiry(cf, data, &pktx); + if(result) { + *err = result; + sent = -1; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, sent, *err); CF_DATA_RESTORE(cf, save); return sent; } @@ -1763,35 +1926,28 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf, return result; } -struct recv_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - size_t pkt_count; -}; - static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, struct sockaddr_storage *remote_addr, socklen_t remote_addrlen, int ecn, void *userp) { - struct recv_ctx *r = userp; - struct cf_ngtcp2_ctx *ctx = r->cf->ctx; + struct pkt_io_ctx *pktx = userp; + struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; ngtcp2_pkt_info pi; ngtcp2_path path; int rv; - ++r->pkt_count; + ++pktx->pkt_count; 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 = (uint32_t)ecn; + pi.ecn = (uint8_t)ecn; - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, r->ts); + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); if(rv) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, read_pkt -> %s", - ngtcp2_strerror(rv))); + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", + ngtcp2_strerror(rv)); if(!ctx->last_error.error_code) { if(rv == NGTCP2_ERR_CRYPTO) { ngtcp2_ccerr_set_tls_alert(&ctx->last_error, @@ -1813,41 +1969,49 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, return CURLE_OK; } -static CURLcode cf_process_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) +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 recv_ctx rctx; + struct pkt_io_ctx local_pktx; size_t pkts_chunk = 128, i; size_t pkts_max = 10 * pkts_chunk; - CURLcode result; + CURLcode result = CURLE_OK; - rctx.cf = cf; - rctx.data = data; - rctx.ts = timestamp(); - rctx.pkt_count = 0; + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx->ts = timestamp(); + } + +#ifdef USE_OPENSSL + if(!ctx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx); + if(result) + return result; + ctx->x509_store_setup = TRUE; + } +#endif for(i = 0; i < pkts_max; i += pkts_chunk) { - rctx.pkt_count = 0; + pktx->pkt_count = 0; result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, - recv_pkt, &rctx); + recv_pkt, pktx); if(result) /* error */ break; - if(rctx.pkt_count < pkts_chunk) /* got less than we could */ + if(pktx->pkt_count < pkts_chunk) /* got less than we could */ break; /* give egress a chance before we receive more */ - result = cf_flush_egress(cf, data); + result = cf_progress_egress(cf, data, pktx); + if(result) /* error */ + break; } return result; } -struct read_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - ngtcp2_path_storage *ps; -}; - /** * Read a network packet to send from ngtcp2 into `buf`. * Return number of bytes written or -1 with *err set. @@ -1856,7 +2020,7 @@ static ssize_t read_pkt_to_send(void *userp, unsigned char *buf, size_t buflen, CURLcode *err) { - struct read_ctx *x = userp; + struct pkt_io_ctx *x = userp; struct cf_ngtcp2_ctx *ctx = x->cf->ctx; nghttp3_vec vec[16]; nghttp3_ssize veccnt; @@ -1896,7 +2060,7 @@ static ssize_t read_pkt_to_send(void *userp, flags = NGTCP2_WRITE_STREAM_FLAG_MORE | (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - n = ngtcp2_conn_writev_stream(ctx->qconn, x->ps? &x->ps->path : NULL, + n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, NULL, buf, buflen, &ndatalen, flags, stream_id, (const ngtcp2_vec *)vec, veccnt, x->ts); @@ -1955,28 +2119,25 @@ static ssize_t read_pkt_to_send(void *userp, return nwritten; } -static CURLcode cf_flush_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) +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; - int rv; ssize_t nread; size_t max_payload_size, path_max_payload_size, max_pktcnt; size_t pktcnt = 0; size_t gsolen = 0; /* this disables gso until we have a clue */ - ngtcp2_path_storage ps; - ngtcp2_tstamp ts = timestamp(); - ngtcp2_tstamp expiry; - ngtcp2_duration timeout; CURLcode curlcode; - struct read_ctx readx; + struct pkt_io_ctx local_pktx; - rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); - return CURLE_SEND_ERROR; + if(!pktx) { + pktx_init(&local_pktx, cf, data); + pktx = &local_pktx; + } + else { + pktx->ts = timestamp(); + ngtcp2_path_storage_zero(&pktx->ps); } curlcode = vquic_flush(cf, data, &ctx->q); @@ -1988,8 +2149,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, return curlcode; } - ngtcp2_path_storage_zero(&ps); - /* In UDP, there is a maximum theoretical packet paload length and * a minimum payload length that is "guarantueed" to work. * To detect if this minimum payload can be increased, ngtcp2 sends @@ -2008,17 +2167,10 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, max_pktcnt = CURLMIN(MAX_PKT_BURST, ctx->q.sendbuf.chunk_size / max_payload_size); - readx.cf = cf; - readx.data = data; - readx.ts = ts; - readx.ps = &ps; - for(;;) { /* add the next packet to send, if any, to our buffer */ nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, &readx, &curlcode); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - max_payload_size, nread, curlcode)); */ + read_pkt_to_send, pktx, &curlcode); if(nread < 0) { if(curlcode != CURLE_AGAIN) return curlcode; @@ -2076,21 +2228,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, } out: - /* non-errored exit. check when we should run again. */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= ts) { - timeout = 0; - } - else { - timeout = expiry - ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - } - Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); - } - return CURLE_OK; } @@ -2101,7 +2238,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - const struct stream_ctx *stream = H3_STREAM_CTX(data); + const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)cf; return stream && !Curl_bufq_is_empty(&stream->recvbuf); } @@ -2113,7 +2250,7 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf, /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge * the streams windows. As we do in HTTP/2. */ if(!pause) { - drain_stream(cf, data); + h3_drain_stream(cf, data); Curl_expire(data, 0, EXPIRE_RUN_NOW); } return CURLE_OK; @@ -2141,7 +2278,7 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, break; } case CF_CTRL_DATA_DONE_SEND: { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); if(stream && !stream->send_closed) { stream->send_closed = TRUE; stream->upload_left = Curl_bufq_len(&stream->sendbuf); @@ -2149,13 +2286,16 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, } break; } - case CF_CTRL_DATA_IDLE: - if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) { - if(cf_flush_egress(cf, data)) { - result = CURLE_SEND_ERROR; - } + case CF_CTRL_DATA_IDLE: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + CURL_TRC_CF(data, cf, "data idle"); + if(stream && !stream->closed) { + result = check_and_set_expiry(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result); } break; + } default: break; } @@ -2212,7 +2352,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) ngtcp2_tstamp ts; ngtcp2_ssize rc; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); ts = timestamp(); rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ NULL, /* pkt_info */ @@ -2236,7 +2376,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_ngtcp2_ctx_clear(ctx); free(ctx); @@ -2250,13 +2390,14 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) * Might be called twice for happy eyeballs. */ static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data) + 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; + const struct Curl_sockaddr_ex *sockaddr = NULL; int qfd; ctx->version = NGTCP2_PROTO_VER_MAX; @@ -2294,7 +2435,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); ctx->qlogfd = qfd; /* -1 if failure above */ - quic_settings(ctx, data); + quic_settings(ctx, data, pktx); result = vquic_ctx_init(&ctx->q); if(result) @@ -2302,6 +2443,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL, NULL, NULL, NULL); + if(!sockaddr) + 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); @@ -2344,6 +2487,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; struct cf_call_data save; struct curltime now; + struct pkt_io_ctx pktx; if(cf->connected) { *done = TRUE; @@ -2359,40 +2503,41 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, *done = FALSE; now = Curl_now(); + pktx_init(&pktx, cf, data); CF_DATA_SAVE(save, cf, data); if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; } if(!ctx->qconn) { ctx->started_at = now; - result = cf_connect_start(cf, data); + result = cf_connect_start(cf, data, &pktx); if(result) goto out; - result = cf_flush_egress(cf, data); + result = cf_progress_egress(cf, data, &pktx); /* we do not expect to be able to recv anything yet */ goto out; } - result = cf_process_ingress(cf, data); + result = cf_progress_ingress(cf, data, &pktx); if(result) goto out; - result = cf_flush_egress(cf, data); + result = cf_progress_egress(cf, data, &pktx); if(result) goto out; if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { ctx->handshake_at = now; - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); result = qng_verify_peer(cf, data); if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); + CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -2402,7 +2547,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, out: if(result == CURLE_RECV_ERROR && ctx->qconn && - ngtcp2_conn_is_in_draining_period(ctx->qconn)) { + ngtcp2_conn_in_draining_period(ctx->qconn)) { /* When a QUIC server instance is shutting down, it may send us a * CONNECTION_CLOSE right away. Our connection then enters the DRAINING * state. @@ -2414,8 +2559,8 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, */ int reconn_delay_ms = 200; - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); + CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms); Curl_conn_cf_close(cf->next, data); cf_ngtcp2_ctx_clear(ctx); result = Curl_conn_cf_connect(cf->next, data, FALSE, done); @@ -2430,8 +2575,8 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); @@ -2439,7 +2584,11 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, r_ip, r_port, curl_easy_strerror(result)); } #endif - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + 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; } @@ -2463,7 +2612,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, INT_MAX : (int)rp->initial_max_streams_bidi; else /* not arrived yet? */ *pres1 = Curl_multi_max_concurrent_streams(data->multi); - DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1)); + CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); CF_DATA_RESTORE(cf, save); return CURLE_OK; } @@ -2510,13 +2659,11 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ *input_pending = FALSE; - Curl_attach_connection(data, cf->conn); - if(cf_process_ingress(cf, data)) + if(cf_progress_ingress(cf, data, NULL)) alive = FALSE; else { alive = TRUE; } - Curl_detach_connection(data); } return alive; diff --git a/vendor/curl/lib/vquic/curl_quiche.c b/vendor/curl/lib/vquic/curl_quiche.c index 3a4f9f9f20..3598de1c7a 100644 --- a/vendor/curl/lib/vquic/curl_quiche.c +++ b/vendor/curl/lib/vquic/curl_quiche.c @@ -45,8 +45,10 @@ #include "vquic_int.h" #include "curl_quiche.h" #include "transfer.h" +#include "inet_pton.h" #include "vtls/openssl.h" #include "vtls/keylog.h" +#include "vtls/vtls.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -56,7 +58,7 @@ /* #define DEBUG_QUICHE */ #define QUIC_MAX_STREAMS (100) -#define QUIC_IDLE_TIMEOUT (5 * 1000) /* milliseconds */ +#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) @@ -88,54 +90,6 @@ static void keylog_callback(const SSL *ssl, const char *line) Curl_tls_keylog_write_line(line); } -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - SSL_CTX_set_alpn_protos(ssl_ctx, - (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - - SSL_CTX_set_default_verify_paths(ssl_ctx); - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - { - struct connectdata *conn = data->conn; - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - } - return ssl_ctx; -} - struct cf_quiche_ctx { struct cf_quic_ctx q; quiche_conn *qconn; @@ -154,6 +108,7 @@ struct cf_quiche_ctx { size_t sends_on_hold; /* # of streams with SEND_HOLD set */ BIT(goaway); /* got GOAWAY from server */ BIT(got_first_byte); /* if first byte was received */ + BIT(x509_store_setup); /* if x509 store has been set up */ }; #ifdef DEBUG_QUICHE @@ -181,12 +136,96 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) } } +static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + if(!ctx->x509_store_setup) { + if(cf->conn->ssl_config.verifypeer) { + const char * const ssl_cafile = cf->conn->ssl_config.CAfile; + const char * const ssl_capath = cf->conn->ssl_config.CApath; + if(ssl_cafile || ssl_capath) { + SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations( + ctx->sslctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates won't work so + use openssl's built-in default as fallback */ + SSL_CTX_set_default_verify_paths(ssl_ctx); + } +#endif + } + ctx->x509_store_setup = TRUE; + } + return CURLE_OK; +} + +static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + unsigned char checkip[16]; + + DEBUGASSERT(!ctx->sslctx); + ctx->sslctx = SSL_CTX_new(TLS_method()); + if(!ctx->sslctx) + return CURLE_OUT_OF_MEMORY; + + SSL_CTX_set_alpn_protos(ctx->sslctx, + (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + + SSL_CTX_set_default_verify_paths(ctx->sslctx); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback); + } + + ctx->ssl = SSL_new(ctx->sslctx); + if(!ctx->ssl) + return CURLE_QUIC_CONNECT_ERROR; + + SSL_set_app_data(ctx->ssl, cf); + + if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) +#ifdef ENABLE_IPV6 + && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) +#endif + ) { + char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); + if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + failf(data, "Failed set SNI"); + SSL_free(ctx->ssl); + ctx->ssl = NULL; + return CURLE_QUIC_CONNECT_ERROR; + } + } + + return CURLE_OK; +} + /** * All about the H3 internals of a stream */ struct stream_ctx { int64_t id; /* HTTP/3 protocol stream identifier */ struct bufq recvbuf; /* h3 response */ + struct h1_req_parser h1; /* h1 request parsing */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ bool closed; /* TRUE on stream close */ @@ -217,11 +256,10 @@ static void stream_send_suspend(struct Curl_cfilter *cf, data->req.keepon |= KEEP_SEND_HOLD; ++ctx->sends_on_hold; if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] suspend sending", - H3_STREAM_ID(data))); + CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending", + H3_STREAM_ID(data)); else - DEBUGF(LOG_CF(data, cf, "[%s] suspend sending", - data->state.url)); + CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url); } } @@ -234,11 +272,10 @@ static void stream_send_resume(struct Curl_cfilter *cf, data->req.keepon &= ~KEEP_SEND_HOLD; --ctx->sends_on_hold; if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] resume sending", - H3_STREAM_ID(data))); + CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending", + H3_STREAM_ID(data)); else - DEBUGF(LOG_CF(data, cf, "[%s] resume sending", - data->state.url)); + CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url); Curl_expire(data, 0, EXPIRE_RUN_NOW); } } @@ -277,7 +314,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->id = -1; Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data)); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); return CURLE_OK; } @@ -288,13 +325,13 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); if(stream_send_is_suspended(data)) { data->req.keepon &= ~KEEP_SEND_HOLD; --ctx->sends_on_hold; } Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); free(stream); H3_STREAM_LCTX(data) = NULL; } @@ -329,7 +366,7 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, else { DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { - if(H3_STREAM_ID(sdata) == stream3_id) { + if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) { return sdata; } } @@ -379,8 +416,12 @@ static int cb_each_header(uint8_t *name, size_t name_len, struct stream_ctx *stream = H3_STREAM_CTX(x->data); CURLcode result; - (void)stream; + if(!stream) + return CURLE_OK; + if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s", + stream->id, (int)value_len, value); result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); if(!result) result = write_resp_raw(x->cf, x->data, value, value_len); @@ -388,6 +429,9 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, " \r\n", 3); } else { + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] header: %.*s: %.*s", + stream->id, (int)name_len, name, + (int)value_len, value); result = write_resp_raw(x->cf, x->data, name, name_len); if(!result) result = write_resp_raw(x->cf, x->data, ": ", 2); @@ -397,10 +441,8 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, "\r\n", 2); } if(result) { - DEBUGF(LOG_CF(x->data, x->cf, - "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d", - stream? stream->id : -1, (int)name_len, name, - (int)value_len, value, result)); + CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d", + stream->id, result); } return result; } @@ -425,12 +467,8 @@ static ssize_t stream_resp_read(void *reader_ctx, *err = CURLE_OK; return nread; } - else if(nread < 0) { - *err = CURLE_AGAIN; - return -1; - } else { - *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + *err = CURLE_AGAIN; return -1; } } @@ -459,10 +497,10 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result); if(nwritten < 0 && result != CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd", - stream->id, nwritten)); - failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]", - nwritten, stream->id); + CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd", + stream->id, nwritten); + failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]", + result, stream->id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -518,7 +556,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, rc, stream3_id); return CURLE_RECV_ERROR; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id); break; case QUICHE_H3_EVENT_DATA: @@ -528,7 +566,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_RESET: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -536,7 +574,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_FINISHED: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id); if(!stream->resp_hds_complete) { result = write_resp_raw(cf, data, "\r\n", 2); if(result) @@ -545,15 +583,16 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, } stream->closed = TRUE; streamclose(cf->conn, "End of stream"); + data->req.keepon &= ~KEEP_SEND_HOLD; break; case QUICHE_H3_EVENT_GOAWAY: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id); break; default: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d", - stream3_id, quiche_h3_event_type(ev))); + CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d", + stream3_id, quiche_h3_event_type(ev)); break; } return result; @@ -575,28 +614,32 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, break; } else if(stream3_id < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error poll: %"PRId64, - stream? stream->id : -1, stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64, + stream? stream->id : -1, stream3_id); return CURLE_HTTP3; } sdata = get_stream_easy(cf, data, stream3_id); if(!sdata) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] discard event %s for " - "unknown [h3sid=%"PRId64"]", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for " + "unknown [%"PRId64"]", + stream? stream->id : -1, cf_ev_name(ev), stream3_id); } else { result = h3_process_event(cf, sdata, stream3_id, ev); drain_stream(cf, sdata); if(result) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error processing event %s " - "for [h3sid=%"PRId64"] -> %d", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id, result)); - quiche_h3_event_free(ev); - return result; + CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s " + "for [%"PRId64"] -> %d", + stream? stream->id : -1, cf_ev_name(ev), + stream3_id, result); + if(data == sdata) { + /* Only report this error to the caller if it is about the + * transfer we were called with. Otherwise we fail a transfer + * due to a problem in another one. */ + quiche_h3_event_free(ev); + return result; + } } quiche_h3_event_free(ev); } @@ -632,7 +675,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, &recv_info); if(nread < 0) { if(QUICHE_ERR_DONE == nread) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche is DONE")); + CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); return CURLE_OK; } else if(QUICHE_ERR_TLS_FAIL == nread) { @@ -649,8 +692,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, } } else if((size_t)nread < pktlen) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zd bytes", - nread, pktlen)); + CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", + nread, pktlen); } return CURLE_OK; @@ -664,6 +707,10 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, CURLcode result; DEBUGASSERT(ctx->qconn); + result = quic_x509_store_setup(cf, data); + if(result) + return result; + rctx.cf = cf; rctx.data = data; rctx.pkts = 0; @@ -719,10 +766,20 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; ssize_t nread; CURLcode result; + int64_t expiry_ns; int64_t timeout_ns; struct read_ctx readx; size_t pkt_count, gsolen; + expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn); + if(!expiry_ns) { + quiche_conn_on_timeout(ctx->qconn); + if(quiche_conn_is_closed(ctx->qconn)) { + failf(data, "quiche_conn_on_timeout closed the connection"); + return CURLE_SEND_ERROR; + } + } + result = vquic_flush(cf, data, &ctx->q); if(result) { if(result == CURLE_AGAIN) { @@ -741,9 +798,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, /* add the next packet to send, if any, to our buffer */ nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, read_pkt_to_send, &readx, &result); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - (size_t)0, nread, result)); */ - if(nread < 0) { if(result != CURLE_AGAIN) return result; @@ -794,8 +848,8 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d", + stream->id, *err); } else if(!stream->resp_got_header) { failf(data, @@ -804,14 +858,12 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, stream->id); /* *err = CURLE_PARTIAL_FILE; */ *err = CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete" + " -> %d", stream->id, *err); } else { *err = CURLE_OK; nread = 0; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); } return nread; } @@ -826,20 +878,20 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!stream) { *err = CURLE_RECV_ERROR; - goto out; + return -1; } if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; } if(cf_process_ingress(cf, data)) { - DEBUGF(LOG_CF(data, cf, "cf_recv, error on ingress")); + CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); *err = CURLE_RECV_ERROR; nread = -1; goto out; @@ -849,8 +901,8 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; } @@ -877,14 +929,15 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = cf_flush_egress(cf, data); if(result) { - DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed")); + CURL_TRC_CF(data, cf, "cf_recv, flush egress failed"); *err = result; nread = -1; } if(nread > 0) ctx->data_recvd += nread; - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%zd) -> %zd, %d", - stream->id, ctx->data_recvd, nread, *err)); + CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%" + CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", + stream->id, ctx->data_recvd, nread, *err); return nread; } @@ -901,7 +954,6 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, struct stream_ctx *stream = H3_STREAM_CTX(data); size_t nheader, i; int64_t stream3_id; - struct h1_req_parser h1; struct dynhds h2_headers; quiche_h3_header *nva = NULL; ssize_t nwritten; @@ -909,28 +961,31 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(!stream) { *err = h3_data_setup(cf, data); if(*err) { - nwritten = -1; - goto out; + return -1; } stream = H3_STREAM_CTX(data); DEBUGASSERT(stream); } - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); DEBUGASSERT(stream); - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(quiche_h3_header) * nheader); @@ -973,16 +1028,16 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - DEBUGF(LOG_CF(data, cf, "send_request(%s) rejected with BLOCKED", - data->state.url)); + CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED", + data->state.url); stream_send_suspend(cf, data); *err = CURLE_AGAIN; nwritten = -1; goto out; } else { - DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64, - data->state.url, stream3_id)); + CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, + data->state.url, stream3_id); } *err = CURLE_SEND_ERROR; nwritten = -1; @@ -995,14 +1050,18 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, stream->closed = FALSE; stream->reset = FALSE; - infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)", - stream3_id, (void *)data); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream3_id, data->state.url)); + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].name_len, nva[i].name, + (int)nva[i].value_len, nva[i].value); + } + } out: free(nva); - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -1036,24 +1095,40 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* TODO: we seem to be blocked on flow control and should HOLD * sending. But when do we open again? */ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> window exhausted", stream->id, len)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> window exhausted", stream->id, len); stream_send_suspend(cf, data); } *err = CURLE_AGAIN; nwritten = -1; goto out; } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE && + stream->closed && stream->resp_hds_complete) { + /* sending request body on a stream that has been closed by the + * server. If the server has send us a final response, we should + * silently discard the send data. + * This happens for example on redirects where the server, instead + * of reading the full request body just closed the stream after + * sending the 30x response. + * This is sort of a race: had the transfer loop called recv first, + * it would see the response and stop/discard sending on its own- */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> exceeds size", stream->id, len); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } else if(nwritten < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> quiche err %zd", stream->id, len, nwritten); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; @@ -1067,9 +1142,9 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream->upload_left == 0) stream->send_closed = TRUE; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, " - "left=%zd) -> %zd", - stream->id, len, stream->upload_left, nwritten)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, " + "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", + stream->id, len, stream->upload_left, nwritten); *err = CURLE_OK; } } @@ -1080,8 +1155,8 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = result; nwritten = -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nwritten, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, nwritten, *err); return nwritten; } @@ -1151,10 +1226,8 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, (void)arg1; (void)arg2; switch(event) { - case CF_CTRL_DATA_SETUP: { - result = h3_data_setup(cf, data); + case CF_CTRL_DATA_SETUP: break; - } case CF_CTRL_DATA_PAUSE: result = h3_data_pause(cf, data, (arg1 != 0)); break; @@ -1172,16 +1245,20 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, stream->upload_left = 0; body[0] = 'X'; sent = cf_quiche_send(cf, data, body, 0, &result); - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d", - stream->id, sent, result)); + CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d", + stream->id, sent, result); } break; } - case CF_CTRL_DATA_IDLE: - result = cf_flush_egress(cf, data); - if(result) - DEBUGF(LOG_CF(data, cf, "data idle, flush egress -> %d", result)); + case CF_CTRL_DATA_IDLE: { + struct stream_ctx *stream = H3_STREAM_CTX(data); + if(stream && !stream->closed) { + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result); + } break; + } default: break; } @@ -1211,7 +1288,7 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf, goto out; } else - DEBUGF(LOG_CF(data, cf, "Skipped certificate verification")); + CURL_TRC_CF(data, cf, "Skipped certificate verification"); ctx->h3config = quiche_h3_config_new(); if(!ctx->h3config) { @@ -1299,15 +1376,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->ssl); DEBUGASSERT(!ctx->sslctx); - ctx->sslctx = quic_ssl_ctx(data); - if(!ctx->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - ctx->ssl = SSL_new(ctx->sslctx); - if(!ctx->ssl) - return CURLE_QUIC_CONNECT_ERROR; - - SSL_set_app_data(ctx->ssl, cf); - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + result = quic_ssl_setup(cf, data); + if(result) + return result; result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); if(result) @@ -1343,11 +1414,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, } #endif - /* we do not get a setup event for the initial transfer */ - result = h3_data_setup(cf, data); - if(result) - return result; - result = cf_flush_egress(cf, data); if(result) return result; @@ -1363,8 +1429,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, offset += 1 + alpn_len; } - DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", - alpn_protocols + 1)); + CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", + alpn_protocols + 1); } return CURLE_OK; @@ -1395,7 +1461,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; } @@ -1418,12 +1484,12 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, goto out; if(quiche_conn_is_established(ctx->qconn)) { - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); ctx->handshake_at = now; result = cf_verify_peer(cf, data); if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); + CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -1442,8 +1508,8 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, */ int reconn_delay_ms = 200; - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); + CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms); Curl_conn_cf_close(cf->next, data); cf_quiche_ctx_clear(ctx); result = Curl_conn_cf_connect(cf->next, data, FALSE, done); @@ -1509,7 +1575,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; - DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1)); + CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: @@ -1555,13 +1621,11 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, not in use by any other transfer, there shouldn't be any data here, only "protocol frames" */ *input_pending = FALSE; - Curl_attach_connection(data, cf->conn); if(cf_process_ingress(cf, data)) alive = FALSE; else { alive = TRUE; } - Curl_detach_connection(data); } return alive; diff --git a/vendor/curl/lib/vquic/vquic.c b/vendor/curl/lib/vquic/vquic.c index f850029a62..9a1a1bbb3c 100644 --- a/vendor/curl/lib/vquic/vquic.c +++ b/vendor/curl/lib/vquic/vquic.c @@ -43,12 +43,14 @@ #include "bufq.h" #include "dynbuf.h" #include "cfilters.h" -#include "curl_log.h" +#include "curl_trc.h" #include "curl_msh3.h" #include "curl_ngtcp2.h" #include "curl_quiche.h" +#include "rand.h" #include "vquic.h" #include "vquic_int.h" +#include "strerror.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -88,6 +90,16 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) #else qctx->no_gso = TRUE; #endif +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_DBG_QUIC_WBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + qctx->wblock_percent = (int)l; + } + } +#endif return CURLE_OK; } @@ -230,6 +242,17 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(qctx->wblock_percent > 0) { + unsigned char c; + Curl_rand(data, &c, 1); + if(c >= ((100-qctx->wblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); + return CURLE_AGAIN; + } + } +#endif if(qctx->no_gso && pktlen > gsolen) { return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } @@ -253,11 +276,9 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len; } - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)", - blen, gsolen)); result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", - blen, gsolen, result, sent)); + CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", + blen, gsolen, result, sent); if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -288,9 +309,9 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; qctx->split_gsolen = gsolen; qctx->gsolen = tail_gsolen; - DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", - qctx->split_len, qctx->split_gsolen, - tail_len, qctx->gsolen)); + CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", + qctx->split_len, qctx->split_gsolen, + tail_len, qctx->gsolen); return vquic_flush(cf, data, qctx); } @@ -308,6 +329,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr[MMSG_NUM]; size_t total_nread, pkts; int mcount, i, n; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; DEBUGASSERT(max_pkts > 0); @@ -330,12 +352,12 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, ; if(mcount == -1) { if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN")); + CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -343,13 +365,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)", - mcount, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)", + mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } - DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount)); + CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); pkts += mcount; for(i = 0; i < mcount; ++i) { total_nread += mmsg[i].msg_len; @@ -362,8 +385,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } @@ -380,6 +404,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr; size_t total_nread, pkts; ssize_t nread; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; msg_iov.iov_base = buf; @@ -401,8 +426,8 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -410,8 +435,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -425,8 +451,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } @@ -443,6 +470,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, socklen_t remote_addrlen = sizeof(remote_addr); size_t total_nread, pkts; ssize_t nread; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; DEBUGASSERT(max_pkts > 0); @@ -454,12 +482,12 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, ; if(nread == -1) { if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN")); + CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -467,8 +495,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -482,8 +511,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ diff --git a/vendor/curl/lib/vquic/vquic_int.h b/vendor/curl/lib/vquic/vquic_int.h index 8e08784e7d..dbcd009d72 100644 --- a/vendor/curl/lib/vquic/vquic_int.h +++ b/vendor/curl/lib/vquic/vquic_int.h @@ -41,6 +41,9 @@ struct cf_quic_ctx { size_t gsolen; /* length of individual packets in send buf */ size_t split_len; /* if != 0, buffer length after which GSO differs */ size_t split_gsolen; /* length of individual packets after split_len */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ +#endif bool no_gso; /* do not use gso on sending */ }; diff --git a/vendor/curl/lib/vssh/libssh.c b/vendor/curl/lib/vssh/libssh.c index 1cecb649cb..dea0084575 100644 --- a/vendor/curl/lib/vssh/libssh.c +++ b/vendor/curl/lib/vssh/libssh.c @@ -40,9 +40,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -2186,7 +2183,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) myssh_setup_connection(data, conn); /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); if(conn->handler->protocol & CURLPROTO_SCP) { diff --git a/vendor/curl/lib/vssh/libssh2.c b/vendor/curl/lib/vssh/libssh2.c index 14c2784fe3..37040b4b77 100644 --- a/vendor/curl/lib/vssh/libssh2.c +++ b/vendor/curl/lib/vssh/libssh2.c @@ -43,9 +43,6 @@ #ifdef HAVE_ARPA_INET_H #include #endif -#ifdef HAVE_UTSNAME_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -100,11 +97,9 @@ /* Local functions: */ static const char *sftp_libssh2_strerror(unsigned long err); -#ifdef CURL_LIBSSH2_DEBUG static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); static LIBSSH2_FREE_FUNC(my_libssh2_free); -#endif static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data); static CURLcode ssh_connect(struct Curl_easy *data, bool *done); static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); @@ -284,8 +279,6 @@ static CURLcode libssh2_session_error_to_CURLE(int err) return CURLE_SSH; } -#ifdef CURL_LIBSSH2_DEBUG - static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc) { (void)abstract; /* arg not used */ @@ -305,8 +298,6 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free) free(ptr); } -#endif - /* * SSH State machine related code */ @@ -895,6 +886,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) } if(found) { + int rc; infof(data, "Found host %s in %s", conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); @@ -944,9 +936,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) } infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); - result = libssh2_session_error_to_CURLE( - libssh2_session_method_pref( - sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method)); + rc = libssh2_session_method_pref(sshc->ssh_session, + LIBSSH2_METHOD_HOSTKEY, hostkey_method); + if(rc) { + char *errmsg = NULL; + int errlen; + libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0); + failf(data, "libssh2: %s", errmsg); + result = libssh2_session_error_to_CURLE(rc); + } } else { infof(data, "Did not find host %s in %s", @@ -2536,7 +2534,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) if(from > size) { failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize); + CURL_FORMAT_CURL_OFF_T ")", from, + (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } if(from > to) { @@ -2562,7 +2561,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, attrs.filesize); + data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } /* download from where? */ @@ -2572,7 +2571,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) if((curl_off_t)attrs.filesize < data->state.resume_from) { failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, attrs.filesize); + data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } } @@ -3253,7 +3252,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) } /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); sshc = &conn->proto.sshc; @@ -3268,13 +3267,12 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ -#ifdef CURL_LIBSSH2_DEBUG + /* libcurl MUST to set custom memory functions so that the kbd_callback + funciton's memory allocations can be properled freed */ sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, my_libssh2_free, my_libssh2_realloc, data); -#else - sshc->ssh_session = libssh2_session_init_ex(NULL, NULL, NULL, data); -#endif + if(!sshc->ssh_session) { failf(data, "Failure initialising ssh session"); return CURLE_FAILED_INIT; diff --git a/vendor/curl/lib/vssh/wolfssh.c b/vendor/curl/lib/vssh/wolfssh.c index 780b6126ac..306d299bcf 100644 --- a/vendor/curl/lib/vssh/wolfssh.c +++ b/vendor/curl/lib/vssh/wolfssh.c @@ -277,7 +277,7 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, return -1; } DEBUGASSERT(rc == (int)len); - infof(data, "sent %zd bytes SFTP from offset %zd", + infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T, len, sshc->offset); sshc->offset += len; return (ssize_t)rc; @@ -374,7 +374,7 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done) wssh_setup_connection(data, conn); /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); if(conn->handler->protocol & CURLPROTO_SCP) { diff --git a/vendor/curl/lib/vtls/bearssl.c b/vendor/curl/lib/vtls/bearssl.c index 2b666ca6fe..934149c1b0 100644 --- a/vendor/curl/lib/vtls/bearssl.c +++ b/vendor/curl/lib/vtls/bearssl.c @@ -52,7 +52,7 @@ struct x509_context { int cert_num; }; -struct ssl_backend_data { +struct bearssl_ssl_backend_data { br_ssl_client_context ctx; struct x509_context x509; unsigned char buf[BR_SSL_BUFSIZE_BIDI]; @@ -574,7 +574,8 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; @@ -586,6 +587,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const bool verifyhost = conn_config->verifyhost; CURLcode ret; unsigned version_min, version_max; + int session_set = 0; #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -593,6 +595,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, #endif DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step1"); switch(conn_config->version) { case CURL_SSLVERSION_SSLv2: @@ -623,38 +626,34 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if(ca_info_blob) { - struct cafile_source source; - source.type = CAFILE_SOURCE_BLOB; - source.data = ca_info_blob->data; - source.len = ca_info_blob->len; + if(verifypeer) { + if(ca_info_blob) { + struct cafile_source source; + source.type = CAFILE_SOURCE_BLOB; + source.data = ca_info_blob->data; + source.len = ca_info_blob->len; - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { + CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { failf(data, "error importing CA certificate blob"); return ret; } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); } - } - if(ssl_cafile) { - struct cafile_source source; - source.type = CAFILE_SOURCE_PATH; - source.data = ssl_cafile; - source.len = 0; + if(ssl_cafile) { + struct cafile_source source; + source.type = CAFILE_SOURCE_PATH; + source.data = ssl_cafile; + source.len = 0; - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { + CURL_TRC_CF(data, cf, "connect_step1, load cafile"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { failf(data, "error setting certificate verify locations." " CAfile: %s", ssl_cafile); return ret; } - infof(data, "error setting certificate verify locations," - " continuing anyway:"); } } @@ -668,6 +667,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(conn_config->cipher_list) { /* Override the ciphers as specified. For the default cipher list see the BearSSL source code of br_ssl_client_init_full() */ + CURL_TRC_CF(data, cf, "connect_step1, set ciphers"); ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, conn_config->cipher_list); if(ret) @@ -683,10 +683,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(ssl_config->primary.sessionid) { void *session; + CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); - infof(data, "BearSSL: re-using session ID"); + session_set = 1; + infof(data, "BearSSL: reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -723,6 +725,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } hostname = snihost; + CURL_TRC_CF(data, cf, "connect_step1, SNI set"); } /* give application a chance to interfere with SSL set up. */ @@ -737,7 +740,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, } } - if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) + if(!br_ssl_client_reset(&backend->ctx, hostname, session_set)) return CURLE_FAILED_INIT; backend->active = TRUE; @@ -746,12 +749,35 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } +static int bearssl_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + else { + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); + if(state & BR_SSL_SENDREC) { + socks[0] = sock; + return GETSOCK_WRITESOCK(0); + } + } + socks[0] = sock; + return GETSOCK_READSOCK(0); +} + static CURLcode bearssl_run_until(struct Curl_cfilter *cf, struct Curl_easy *data, unsigned target) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; unsigned state; unsigned char *buf; size_t len; @@ -796,6 +822,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, if(state & BR_SSL_SENDREC) { buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); if(ret <= 0) { return result; } @@ -804,6 +831,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, else if(state & BR_SSL_RECVREC) { buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); if(ret == 0) { failf(data, "SSL: EOF without close notify"); return CURLE_READ_ERROR; @@ -820,20 +848,31 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; CURLcode ret; DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step2"); ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) return CURLE_OK; if(ret == CURLE_OK) { + unsigned int tver; if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_3; + /* Informational message */ + tver = br_ssl_engine_get_version(&backend->ctx.eng); + if(tver == 0x0303) + infof(data, "SSL connection using TLSv1.2"); + else if(tver == 0x0304) + infof(data, "SSL connection using TLSv1.3"); + else + infof(data, "SSL connection using TLS 0x%x", tver); } return ret; } @@ -842,12 +881,14 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode ret; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step3"); if(connssl->alpn) { const char *proto; @@ -889,7 +930,8 @@ static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; unsigned char *app; size_t applen; @@ -923,7 +965,8 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; unsigned char *app; size_t applen; @@ -954,8 +997,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, timediff_t timeout_ms; int what; + CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking); /* check if the connection has already been established */ if(ssl_connection_complete == connssl->state) { + CURL_TRC_CF(data, cf, "connect_common, connected"); *done = TRUE; return CURLE_OK; } @@ -987,8 +1032,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + CURL_TRC_CF(data, cf, "connect_common, check socket"); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0:timeout_ms); + CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1050,10 +1097,12 @@ static bool bearssl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct bearssl_ssl_backend_data *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - return br_ssl_engine_current_state(&ctx->backend->ctx.eng) & BR_SSL_RECVAPP; + backend = (struct bearssl_ssl_backend_data *)ctx->backend; + return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; } static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, @@ -1101,7 +1150,8 @@ static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf, static void *bearssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); return &backend->ctx; } @@ -1109,7 +1159,8 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl, static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; size_t i; DEBUGASSERT(backend); @@ -1147,7 +1198,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input, const struct Curl_ssl Curl_ssl_bearssl = { { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct bearssl_ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ @@ -1159,7 +1210,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + bearssl_get_select_socks, /* getsock */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/vendor/curl/lib/vtls/gskit.c b/vendor/curl/lib/vtls/gskit.c deleted file mode 100644 index 749dc916ad..0000000000 --- a/vendor/curl/lib/vtls/gskit.c +++ /dev/null @@ -1,1327 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_GSKIT - -#include -#include -#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ -#include "socketpair.h" -#include "strerror.h" - -/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ -#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST -#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 -#endif - -#ifndef GSK_TLSV10_CIPHER_SPECS -#define GSK_TLSV10_CIPHER_SPECS 236 -#endif - -#ifndef GSK_TLSV11_CIPHER_SPECS -#define GSK_TLSV11_CIPHER_SPECS 237 -#endif - -#ifndef GSK_TLSV12_CIPHER_SPECS -#define GSK_TLSV12_CIPHER_SPECS 238 -#endif - -#ifndef GSK_PROTOCOL_TLSV11 -#define GSK_PROTOCOL_TLSV11 437 -#endif - -#ifndef GSK_PROTOCOL_TLSV12 -#define GSK_PROTOCOL_TLSV12 438 -#endif - -#ifndef GSK_FALSE -#define GSK_FALSE 0 -#endif - -#ifndef GSK_TRUE -#define GSK_TRUE 1 -#endif - - -#include - -#include -#include "urldata.h" -#include "sendf.h" -#include "gskit.h" -#include "vtls.h" -#include "vtls_int.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "timediff.h" -#include "x509asn1.h" -#include "curl_printf.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - - -/* Directions. */ -#define SOS_READ 0x01 -#define SOS_WRITE 0x02 - -/* SSL version flags. */ -#define CURL_GSKPROTO_SSLV2 0 -#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2) -#define CURL_GSKPROTO_SSLV3 1 -#define CURL_GSKPROTO_SSLV3_MASK (1 << CURL_GSKPROTO_SSLV3) -#define CURL_GSKPROTO_TLSV10 2 -#define CURL_GSKPROTO_TLSV10_MASK (1 << CURL_GSKPROTO_TLSV10) -#define CURL_GSKPROTO_TLSV11 3 -#define CURL_GSKPROTO_TLSV11_MASK (1 << CURL_GSKPROTO_TLSV11) -#define CURL_GSKPROTO_TLSV12 4 -#define CURL_GSKPROTO_TLSV12_MASK (1 << CURL_GSKPROTO_TLSV12) -#define CURL_GSKPROTO_LAST 5 - -struct ssl_backend_data { - gsk_handle handle; - int iocport; - int localfd; - int remotefd; -}; - -#define BACKEND connssl->backend - -/* Supported ciphers. */ -struct gskit_cipher { - const char *name; /* Cipher name. */ - const char *gsktoken; /* Corresponding token for GSKit String. */ - unsigned int versions; /* SSL version flags. */ -}; - -static const struct gskit_cipher ciphertable[] = { - { "null-md5", "01", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha", "02", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc4-md5", "03", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "rc4-md5", "04", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-sha", "05", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc2-cbc-md5", "06", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "exp-des-cbc-sha", "09", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK }, - { "des-cbc3-sha", "0A", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha", "2F", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha", "35", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-gcm-sha256", - "9C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-gcm-sha384", - "9D", CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK }, - { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc2-md5", "4", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc-md5", "6", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc3-md5", "7", CURL_GSKPROTO_SSLV2_MASK }, - { (const char *) NULL, (const char *) NULL, 0 } -}; - - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - - -static CURLcode gskit_status(struct Curl_easy *data, int rc, - const char *procname, CURLcode defcode) -{ - char buffer[STRERROR_LEN]; - /* Process GSKit status and map it to a CURLcode. */ - switch(rc) { - case GSK_OK: - case GSK_OS400_ASYNCHRONOUS_SOC_INIT: - return CURLE_OK; - case GSK_KEYRING_OPEN_ERROR: - case GSK_OS400_ERROR_NO_ACCESS: - return CURLE_SSL_CACERT_BADFILE; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - case GSK_ERROR_BAD_V2_CIPHER: - case GSK_ERROR_BAD_V3_CIPHER: - case GSK_ERROR_NO_CIPHERS: - return CURLE_SSL_CIPHER; - case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: - case GSK_ERROR_CERT_VALIDATION: - return CURLE_PEER_FAILED_VERIFICATION; - case GSK_OS400_ERROR_TIMED_OUT: - return CURLE_OPERATION_TIMEDOUT; - case GSK_WOULD_BLOCK: - return CURLE_AGAIN; - case GSK_OS400_ERROR_NOT_REGISTERED: - break; - case GSK_ERROR_IO: - switch(errno) { - case ENOMEM: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "%s I/O error: %s", procname, - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - } - break; - default: - failf(data, "%s: %s", procname, gsk_strerror(rc)); - break; - } - return defcode; -} - - -static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, - GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_enum(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_enum() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buf, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buf, 0); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_buffer() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_numeric(struct Curl_easy *data, - gsk_handle h, GSK_NUM_ID id, int value) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_numeric_value(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - default: - failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_ciphers(struct Curl_cfilter *cf, struct Curl_easy *data, - gsk_handle h, unsigned int *protoflags) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct connectdata *conn = data->conn; - const char *cipherlist = conn_config->cipher_list; - const char *clp; - const struct gskit_cipher *ctp; - int i; - int l; - bool unsupported; - CURLcode result; - struct { - char *buf; - char *ptr; - } ciphers[CURL_GSKPROTO_LAST]; - - /* Compile cipher list into GSKit-compatible cipher lists. */ - - if(!cipherlist) - return CURLE_OK; - while(is_separator(*cipherlist)) /* Skip initial separators. */ - cipherlist++; - if(!*cipherlist) - return CURLE_OK; - - /* We allocate GSKit buffers of the same size as the input string: since - GSKit tokens are always shorter than their cipher names, allocated buffers - will always be large enough to accommodate the result. */ - l = strlen(cipherlist) + 1; - memset(ciphers, 0, sizeof(ciphers)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - ciphers[i].buf = malloc(l); - if(!ciphers[i].buf) { - while(i--) - free(ciphers[i].buf); - return CURLE_OUT_OF_MEMORY; - } - ciphers[i].ptr = ciphers[i].buf; - *ciphers[i].ptr = '\0'; - } - - /* Process each cipher in input string. */ - unsupported = FALSE; - result = CURLE_OK; - for(;;) { - for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) - cipherlist++; - l = cipherlist - clp; - if(!l) - break; - /* Search the cipher in our table. */ - for(ctp = ciphertable; ctp->name; ctp++) - if(strncasecompare(ctp->name, clp, l) && !ctp->name[l]) - break; - if(!ctp->name) { - failf(data, "Unknown cipher %.*s", l, clp); - result = CURLE_SSL_CIPHER; - } - else { - unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK | - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(ctp->versions & (1 << i)) { - strcpy(ciphers[i].ptr, ctp->gsktoken); - ciphers[i].ptr += strlen(ctp->gsktoken); - } - } - } - - /* Advance to next cipher name or end of string. */ - while(is_separator(*cipherlist)) - cipherlist++; - } - - /* Disable protocols with empty cipher lists. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) { - *protoflags &= ~(1 << i); - ciphers[i].buf[0] = '\0'; - } - } - - /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */ - if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) { - result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.1-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { - result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.2-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - - /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to - the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */ - if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { - result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr, - ciphers[CURL_GSKPROTO_TLSV10].ptr); - } - } - - /* Set-up other ciphers. */ - if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) - result = set_buffer(data, h, GSK_V3_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); - if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) - result = set_buffer(data, h, GSK_V2_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); - - /* Clean-up. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) - free(ciphers[i].buf); - - return result; -} - - -static int gskit_init(void) -{ - /* No initialization needed. */ - return 1; -} - - -static void gskit_cleanup(void) -{ - /* Nothing to do. */ -} - - -static CURLcode init_environment(struct Curl_easy *data, - gsk_handle *envir, const char *appid, - const char *file, const char *label, - const char *password) -{ - int rc; - CURLcode result; - gsk_handle h; - - /* Creates the GSKit environment. */ - - rc = gsk_environment_open(&h); - switch(rc) { - case GSK_OK: - break; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - - result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); - if(!result && appid) - result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); - if(!result && file) - result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); - if(!result && label) - result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); - if(!result && password) - result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); - - if(!result) { - /* Locate CAs, Client certificate and key according to our settings. - Note: this call may be blocking for some tenths of seconds. */ - result = gskit_status(data, gsk_environment_init(h), - "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); - if(!result) { - *envir = h; - return result; - } - } - /* Error: rollback. */ - gsk_environment_close(&h); - return result; -} - - -static void cancel_async_handshake(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - Qso_OverlappedIO_t cstat; - - (void)data; - DEBUGASSERT(BACKEND); - - if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0) - QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); -} - - -static void close_async_handshake(struct ssl_connect_data *connssl) -{ - DEBUGASSERT(BACKEND); - QsoDestroyIOCompletionPort(BACKEND->iocport); - BACKEND->iocport = -1; -} - -static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data, - int directions) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - struct pollfd fds[2]; - int n; - int m; - int i; - int ret = 0; - char buf[CURL_MAX_WRITE_SIZE]; - - DEBUGASSERT(BACKEND); - - if(!connssl_next) - return 0; /* No SSL over SSL: OK. */ - - DEBUGASSERT(connssl_next->backend); - n = 1; - fds[0].fd = BACKEND->remotefd; - fds[1].fd = Curl_conn_cf_get_socket(cf, data); - - if(directions & SOS_READ) { - fds[0].events |= POLLOUT; - } - if(directions & SOS_WRITE) { - n = 2; - fds[0].events |= POLLIN; - fds[1].events |= POLLOUT; - } - i = Curl_poll(fds, n, 0); - if(i < 0) - return -1; /* Select error. */ - - if(fds[0].revents & POLLOUT) { - /* Try getting data from HTTPS proxy and pipe it upstream. */ - n = 0; - i = gsk_secure_soc_read(connssl_next->backend->handle, - buf, sizeof(buf), &n); - switch(i) { - case GSK_OK: - if(n) { - i = write(BACKEND->remotefd, buf, n); - if(i < 0) - return -1; - ret = 1; - } - break; - case GSK_OS400_ERROR_TIMED_OUT: - case GSK_WOULD_BLOCK: - break; - default: - return -1; - } - } - - if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { - /* Pipe data to HTTPS proxy. */ - n = read(BACKEND->remotefd, buf, sizeof(buf)); - if(n < 0) - return -1; - if(n) { - i = gsk_secure_soc_write(connssl_next->backend->handle, buf, n, &m); - if(i != GSK_OK || n != m) - return -1; - ret = 1; - } - } - - return ret; /* OK */ -} - - -static void close_one(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - - DEBUGASSERT(BACKEND); - if(BACKEND->handle) { - gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), - "gsk_secure_soc_close()", 0); - /* Last chance to drain output. */ - while(pipe_ssloverssl(cf, data, SOS_WRITE) > 0) - ; - BACKEND->handle = (gsk_handle) NULL; - if(BACKEND->localfd >= 0) { - close(BACKEND->localfd); - BACKEND->localfd = -1; - } - if(BACKEND->remotefd >= 0) { - close(BACKEND->remotefd); - BACKEND->remotefd = -1; - } - } - if(BACKEND->iocport >= 0) - close_async_handshake(connssl); -} - - -static ssize_t gskit_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, CURLcode *curlcode) -{ - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - CURLcode cc = CURLE_SEND_ERROR; - int written; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(cf, data, SOS_WRITE) >= 0) { - cc = gskit_status(data, - gsk_secure_soc_write(BACKEND->handle, - (char *) mem, (int) len, &written), - "gsk_secure_soc_write()", CURLE_SEND_ERROR); - if(cc == CURLE_OK) - if(pipe_ssloverssl(cf, data, SOS_WRITE) < 0) - cc = CURLE_SEND_ERROR; - } - if(cc != CURLE_OK) { - *curlcode = cc; - written = -1; - } - return (ssize_t) written; /* number of bytes */ -} - - -static ssize_t gskit_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t buffersize, CURLcode *curlcode) -{ - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - int nread; - CURLcode cc = CURLE_RECV_ERROR; - - (void)data; - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(cf, data, SOS_READ) >= 0) { - int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; - cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, - buf, buffsize, &nread), - "gsk_secure_soc_read()", CURLE_RECV_ERROR); - } - switch(cc) { - case CURLE_OK: - break; - case CURLE_OPERATION_TIMEDOUT: - cc = CURLE_AGAIN; - default: - *curlcode = cc; - nread = -1; - break; - } - return (ssize_t) nread; -} - -static CURLcode -set_ssl_version_min_max(unsigned int *protoflags, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct connectdata *conn = data->conn; - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; - long i = ssl_version; - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_TLSv1_2; - break; - } - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - *protoflags |= CURL_GSKPROTO_TLSV10_MASK; - break; - case CURL_SSLVERSION_TLSv1_1: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_2: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "GSKit: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode gskit_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - gsk_handle envir; - CURLcode result; - const char * const keyringfile = conn_config->CAfile; - const char * const keyringpwd = ssl_config->key_passwd; - const char * const keyringlabel = ssl_config->primary.clientcert; - const long int ssl_version = conn_config->version; - const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->hostname; - const char *sni; - unsigned int protoflags = 0; - Qso_OverlappedIO_t commarea; - int sockpair[2]; - static const int sobufsize = CURL_MAX_WRITE_SIZE; - - /* Create SSL environment, start (preferably asynchronous) handshake. */ - DEBUGASSERT(BACKEND); - - BACKEND->handle = (gsk_handle) NULL; - BACKEND->iocport = -1; - BACKEND->localfd = -1; - BACKEND->remotefd = -1; - - /* GSKit supports two ways of specifying an SSL context: either by - * application identifier (that should have been defined at the system - * level) or by keyring file, password and certificate label. - * Local certificate name (CURLOPT_SSLCERT) is used to hold either the - * application identifier of the certificate label. - * Key password (CURLOPT_KEYPASSWD) holds the keyring password. - * It is not possible to have different keyrings for the CAs and the - * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify - * the keyring file. - * If no key password is given and the keyring is the system keyring, - * application identifier mode is tried first, as recommended in IBM doc. - */ - - envir = (gsk_handle) NULL; - - if(keyringlabel && *keyringlabel && !keyringpwd && - !strcmp(keyringfile, CURL_CA_BUNDLE)) { - /* Try application identifier mode. */ - init_environment(data, &envir, keyringlabel, (const char *) NULL, - (const char *) NULL, (const char *) NULL); - } - - if(!envir) { - /* Use keyring mode. */ - result = init_environment(data, &envir, (const char *) NULL, - keyringfile, keyringlabel, keyringpwd); - if(result) - return result; - } - - /* Create secure session. */ - result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle), - "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); - gsk_environment_close(&envir); - if(result) - return result; - - /* Establish a pipelining socket pair for SSL over SSL. */ - if(connssl_next) { - if(Curl_socketpair(0, 0, 0, sockpair)) - return CURLE_SSL_CONNECT_ERROR; - BACKEND->localfd = sockpair[0]; - BACKEND->remotefd = sockpair[1]; - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - curlx_nonblock(BACKEND->localfd, TRUE); - curlx_nonblock(BACKEND->remotefd, TRUE); - } - - /* Determine which SSL/TLS version should be enabled. */ - sni = hostname; - switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - protoflags = CURL_GSKPROTO_SSLV2_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_SSLv3: - protoflags = CURL_GSKPROTO_SSLV3_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - protoflags = CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK; - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - result = set_ssl_version_min_max(&protoflags, cf, data); - if(result != CURLE_OK) - return result; - break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ - if(sni) { - char *snihost = Curl_ssl_snihost(data, sni, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) - result = CURLE_OK; - } - - /* Set session parameters. */ - if(!result) { - /* Compute the handshake timeout. Since GSKit granularity is 1 second, - we round up the required value. */ - timediff_t timeout = Curl_timeleft(data, NULL, TRUE); - if(timeout < 0) - result = CURLE_OPERATION_TIMEDOUT; - else - result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT, - (timeout + 999) / 1000); - } - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1); - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0? - BACKEND->localfd: Curl_conn_cf_get_socket(cf, data)); - if(!result) - result = set_ciphers(cf, data, BACKEND->handle, &protoflags); - if(!protoflags) { - failf(data, "No SSL protocol/cipher combination enabled"); - result = CURLE_SSL_CIPHER; - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2, - (protoflags & CURL_GSKPROTO_SSLV2_MASK)? - GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3, - (protoflags & CURL_GSKPROTO_SSLV3_MASK)? - GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1, - (protoflags & CURL_GSKPROTO_TLSV10_MASK)? - GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11, - (protoflags & CURL_GSKPROTO_TLSV11_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV11_MASK) { - failf(data, "TLS 1.1 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12, - (protoflags & CURL_GSKPROTO_TLSV12_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV12_MASK) { - failf(data, "TLS 1.2 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE, - verifypeer? GSK_SERVER_AUTH_FULL: - GSK_SERVER_AUTH_PASSTHRU, FALSE); - - if(!result) { - /* Start handshake. Try asynchronous first. */ - memset(&commarea, 0, sizeof(commarea)); - BACKEND->iocport = QsoCreateIOCompletionPort(); - if(BACKEND->iocport != -1) { - result = gskit_status(data, - gsk_secure_soc_startInit(BACKEND->handle, - BACKEND->iocport, - &commarea), - "gsk_secure_soc_startInit()", - CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; - } - else - close_async_handshake(connssl); - } - else if(errno != ENOBUFS) - result = gskit_status(data, GSK_ERROR_IO, - "QsoCreateIOCompletionPort()", 0); - else if(connssl_next) { - /* Cannot pipeline while handshaking synchronously. */ - result = CURLE_SSL_CONNECT_ERROR; - } - else { - /* No more completion port available. Use synchronous IO. */ - result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle), - "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_3; - return CURLE_OK; - } - } - } - - /* Error: rollback. */ - close_one(cf, data); - return result; -} - - -static CURLcode gskit_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool nonblocking) -{ - struct ssl_connect_data *connssl = cf->ctx; - Qso_OverlappedIO_t cstat; - struct timeval stmv; - CURLcode result; - - /* Poll or wait for end of SSL asynchronous handshake. */ - DEBUGASSERT(BACKEND); - - for(;;) { - timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); - stmv.tv_sec = 0; - stmv.tv_usec = 0; - if(timeout_ms < 0) - timeout_ms = 0; - switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, - curlx_mstotv(&stmv, timeout_ms))) { - case 1: /* Operation complete. */ - break; - case -1: /* An error occurred: handshake still in progress. */ - if(errno == EINTR) { - if(nonblocking) - return CURLE_OK; - continue; /* Retry. */ - } - if(errno != ETIME) { - char buffer[STRERROR_LEN]; - failf(data, "QsoWaitForIOCompletion() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - cancel_async_handshake(cf, data); - close_async_handshake(connssl); - return CURLE_SSL_CONNECT_ERROR; - } - /* FALL INTO... */ - case 0: /* Handshake in progress, timeout occurred. */ - if(nonblocking) - return CURLE_OK; - cancel_async_handshake(cf, data); - close_async_handshake(connssl); - return CURLE_OPERATION_TIMEDOUT; - } - break; - } - result = gskit_status(data, cstat.returnValue, "SSL handshake", - CURLE_SSL_CONNECT_ERROR); - if(!result) - connssl->connecting_state = ssl_connect_3; - close_async_handshake(connssl); - return result; -} - - -static CURLcode gskit_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - const gsk_cert_data_elem *cdev; - int cdec; - const gsk_cert_data_elem *p; - const char *cert = (const char *) NULL; - const char *certend = (const char *) NULL; - const char *ptr; - CURLcode result; - - /* SSL handshake done: gather certificate info and verify host. */ - DEBUGASSERT(BACKEND); - - if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, - GSK_PARTNER_CERT_INFO, - &cdev, &cdec), - "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == - CURLE_OK) { - int i; - - infof(data, "Server certificate:"); - p = cdev; - for(i = 0; i++ < cdec; p++) - switch(p->cert_data_id) { - case CERT_BODY_DER: - cert = p->cert_data_p; - certend = cert + cdev->cert_data_l; - break; - case CERT_DN_PRINTABLE: - infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_ISSUER_DN_PRINTABLE: - infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_FROM: - infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_TO: - infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p); - break; - } - } - - /* Verify host. */ - result = Curl_verifyhost(cf, data, cert, certend); - if(result) - return result; - - /* The only place GSKit can get the whole CA chain is a validation - callback where no user data pointer is available. Therefore it's not - possible to copy this chain into our structures for CAINFO. - However the server certificate may be available, thus we can return - info about it. */ - if(data->set.ssl.certinfo) { - result = Curl_ssl_init_certinfo(data, 1); - if(result) - return result; - - if(cert) { - result = Curl_extract_certinfo(data, 0, cert, certend); - if(result) - return result; - } - } - - /* Check pinned public key. */ - ptr = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - if(!result && ptr) { - struct Curl_X509certificate x509; - struct Curl_asn1Element *p; - - memset(&x509, 0, sizeof(x509)); - if(Curl_parseX509(&x509, cert, certend)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - p = &x509.subjectPublicKeyInfo; - result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - - -static CURLcode gskit_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool nonblocking, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *done = connssl->state == ssl_connection_complete; - if(*done) - return CURLE_OK; - - /* Step 1: create session, start handshake. */ - if(connssl->connecting_state == ssl_connect_1) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step1(cf, data); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 2: check if handshake is over. */ - if(!result && connssl->connecting_state == ssl_connect_2) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step2(cf, data, nonblocking); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 3: gather certificate info, verify host. */ - if(!result && connssl->connecting_state == ssl_connect_3) - result = gskit_connect_step3(cf, data); - - if(result) - close_one(cf, data); - else if(connssl->connecting_state == ssl_connect_done) { - connssl->state = ssl_connection_complete; - connssl->connecting_state = ssl_connect_1; - *done = TRUE; - } - - return result; -} - - -static CURLcode gskit_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - - result = gskit_connect_common(cf, data, TRUE, done); - if(*done || result) - connssl->connecting_state = ssl_connect_1; - return result; -} - - -static CURLcode gskit_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - bool done; - - connssl->connecting_state = ssl_connect_1; - result = gskit_connect_common(cf, data, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - - -static void gskit_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - close_one(cf, data); -} - - -static int gskit_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - int what; - int rc; - char buf[120]; - int loop = 10; /* don't get stuck */ - - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - close_one(cf, data); - rc = 0; - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); - - while(loop--) { - ssize_t nread; - - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to gsk_secure_soc_read() now, so - use read(). */ - - nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf)); - - if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer))); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); - } - - return rc; -} - - -static size_t gskit_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GSKit"); -} - - -static int gskit_check_cxn(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - int err; - int errlen; - - (void)data; - /* The only thing that can be tested here is at the socket level. */ - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; /* connection has been closed */ - - err = 0; - errlen = sizeof(err); - - if(getsockopt(Curl_conn_cf_get_socket(cf, data), SOL_SOCKET, SO_ERROR, - (unsigned char *) &err, &errlen) || - errlen != sizeof(err) || err) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void *gskit_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - (void)info; - DEBUGASSERT(BACKEND); - return BACKEND->handle; -} - -const struct Curl_ssl Curl_ssl_gskit = { - { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */ - - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct ssl_backend_data), - - gskit_init, /* init */ - gskit_cleanup, /* cleanup */ - gskit_version, /* version */ - gskit_check_cxn, /* check_cxn */ - gskit_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - gskit_connect, /* connect */ - gskit_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ - gskit_get_internals, /* get_internals */ - gskit_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* No session handling for GSKit */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - gskit_recv, /* recv decrypted data */ - gskit_send, /* send data to encrypt */ -}; - -#endif /* USE_GSKIT */ diff --git a/vendor/curl/lib/vtls/gtls.c b/vendor/curl/lib/vtls/gtls.c index 3d1906ea4c..e48346903c 100644 --- a/vendor/curl/lib/vtls/gtls.c +++ b/vendor/curl/lib/vtls/gtls.c @@ -76,7 +76,7 @@ static bool gtls_inited = FALSE; # include -struct ssl_backend_data { +struct gtls_ssl_backend_data { struct gtls_instance gtls; }; @@ -91,7 +91,9 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); if(nwritten < 0) { - gnutls_transport_set_errno(connssl->backend->gtls.session, + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result)? EAGAIN : EINVAL); nwritten = -1; } @@ -109,7 +111,9 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) DEBUGASSERT(data); nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); if(nread < 0) { - gnutls_transport_set_errno(connssl->backend->gtls.session, + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result)? EAGAIN : EINVAL); nread = -1; } @@ -212,7 +216,8 @@ static CURLcode handshake(struct Curl_cfilter *cf, bool nonblocking) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; gnutls_session_t session; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); @@ -679,7 +684,8 @@ static CURLcode gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); long * const pverifyresult = &ssl_config->certverifyresult; @@ -729,7 +735,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) ssl_sessionid, ssl_idsize); /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -1346,7 +1352,8 @@ gtls_connect_common(struct Curl_cfilter *cf, /* Finish connecting once the handshake is done */ if(ssl_connect_1 == connssl->connecting_state) { - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; gnutls_session_t session; DEBUGASSERT(backend); session = backend->gtls.session; @@ -1390,11 +1397,13 @@ static bool gtls_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct gtls_ssl_backend_data *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - if(ctx->backend->gtls.session && - 0 != gnutls_record_check_pending(ctx->backend->gtls.session)) + backend = (struct gtls_ssl_backend_data *)ctx->backend; + if(backend->gtls.session && + 0 != gnutls_record_check_pending(backend->gtls.session)) return TRUE; return FALSE; } @@ -1406,7 +1415,8 @@ static ssize_t gtls_send(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; ssize_t rc; (void)data; @@ -1428,7 +1438,8 @@ static void gtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; (void) data; DEBUGASSERT(backend); @@ -1463,7 +1474,8 @@ static int gtls_shutdown(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; int retval = 0; DEBUGASSERT(backend); @@ -1541,7 +1553,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; ssize_t ret; (void)data; @@ -1620,7 +1633,8 @@ static bool gtls_cert_status_request(void) static void *gtls_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; (void)info; DEBUGASSERT(backend); return backend->gtls.session; @@ -1634,7 +1648,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { SSLSUPP_PINNEDPUBKEY | SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct gtls_ssl_backend_data), gtls_init, /* init */ gtls_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/hostcheck.c b/vendor/curl/lib/vtls/hostcheck.c index d061c6356f..2726dca7f2 100644 --- a/vendor/curl/lib/vtls/hostcheck.c +++ b/vendor/curl/lib/vtls/hostcheck.c @@ -24,8 +24,7 @@ #include "curl_setup.h" -#if defined(USE_OPENSSL) \ - || defined(USE_GSKIT) \ +#if defined(USE_OPENSSL) \ || defined(USE_SCHANNEL) /* these backends use functions from this file */ @@ -133,4 +132,4 @@ bool Curl_cert_hostcheck(const char *match, size_t matchlen, return FALSE; } -#endif /* OPENSSL, GSKIT or schannel+wince */ +#endif /* OPENSSL or SCHANNEL */ diff --git a/vendor/curl/lib/vtls/mbedtls.c b/vendor/curl/lib/vtls/mbedtls.c index d95888c36e..f45636e57e 100644 --- a/vendor/curl/lib/vtls/mbedtls.c +++ b/vendor/curl/lib/vtls/mbedtls.c @@ -81,7 +81,7 @@ # endif #endif -struct ssl_backend_data { +struct mbed_ssl_backend_data { mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_context entropy; mbedtls_ssl_context ssl; @@ -165,8 +165,8 @@ static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen) DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d", - blen, nwritten, result)); + CURL_TRC_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d", + blen, nwritten, result); if(nwritten < 0 && CURLE_AGAIN == result) { nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; } @@ -186,8 +186,8 @@ static int bio_cf_read(void *bio, unsigned char *buf, size_t blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d", - blen, nread, result)); + CURL_TRC_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d", + blen, nread, result); if(nread < 0 && CURLE_AGAIN == result) { nread = MBEDTLS_ERR_SSL_WANT_READ; } @@ -255,7 +255,8 @@ static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); #if MBEDTLS_VERSION_NUMBER >= 0x03000000 int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; @@ -307,7 +308,8 @@ static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); @@ -617,7 +619,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } - infof(data, "mbedTLS re-using session"); + infof(data, "mbedTLS reusing session"); } Curl_ssl_sessionid_unlock(data); } @@ -697,7 +699,8 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { int ret; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const mbedtls_x509_crt *peercert; const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? @@ -860,7 +863,8 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -915,7 +919,8 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; int ret = -1; (void)data; @@ -939,7 +944,8 @@ static void mbedtls_close_all(struct Curl_easy *data) static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; char buf[32]; (void)data; @@ -968,7 +974,8 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; int ret = -1; ssize_t len = -1; @@ -1204,10 +1211,12 @@ static bool mbedtls_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct mbed_ssl_backend_data *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - return mbedtls_ssl_get_bytes_avail(&ctx->backend->ssl) != 0; + backend = (struct mbed_ssl_backend_data *)ctx->backend; + return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; } static CURLcode mbedtls_sha256sum(const unsigned char *input, @@ -1234,7 +1243,8 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, static void *mbedtls_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; (void)info; DEBUGASSERT(backend); return &backend->ssl; @@ -1249,7 +1259,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct mbed_ssl_backend_data), mbedtls_init, /* init */ mbedtls_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/nss.c b/vendor/curl/lib/vtls/nss.c deleted file mode 100644 index 5e5dbb7448..0000000000 --- a/vendor/curl/lib/vtls/nss.c +++ /dev/null @@ -1,2522 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* - * Source file for all NSS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_NSS - -#include "urldata.h" -#include "sendf.h" -#include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ -#include "connect.h" -#include "strcase.h" -#include "select.h" -#include "vtls.h" -#include "vtls_int.h" -#include "llist.h" -#include "multiif.h" -#include "curl_printf.h" -#include "nssg.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for SECKEY_DestroyPublicKey() */ -#include /* for PR_ImportTCPSocket */ - -#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) - -#if NSSVERNUM >= 0x030f00 /* 3.15.0 */ -#include -#endif - -#include "warnless.h" -#include "x509asn1.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define SSL_DIR "/etc/pki/nssdb" - -/* enough to fit the string "PEM Token #[0|1]" */ -#define SLOTSIZE 13 - -struct ssl_backend_data { - PRFileDesc *handle; - char *client_nickname; - struct Curl_easy *data; - struct Curl_llist obj_list; - PK11GenericObject *obj_clicert; -}; - -static PRLock *nss_initlock = NULL; -static PRLock *nss_crllock = NULL; -static PRLock *nss_findslot_lock = NULL; -static PRLock *nss_trustload_lock = NULL; -static struct Curl_llist nss_crl_list; -static NSSInitContext *nss_context = NULL; -static volatile int initialized = 0; - -/* type used to wrap pointers as list nodes */ -struct ptr_list_wrap { - void *ptr; - struct Curl_llist_element node; -}; - -struct cipher_s { - const char *name; - int num; -}; - -#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ - CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ - ptr->type = (_type); \ - ptr->pValue = (_val); \ - ptr->ulValueLen = (_len); \ -} while(0) - -#define CERT_NewTempCertificate __CERT_NewTempCertificate - -#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) -static const struct cipher_s cipherlist[] = { - /* SSL2 cipher suites */ - {"rc4", SSL_EN_RC4_128_WITH_MD5}, - {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, - {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, - {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, - {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, - {"des", SSL_EN_DES_64_CBC_WITH_MD5}, - {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, - /* SSL3/TLS cipher suites */ - {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, - {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, - {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, - {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, - {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, - {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, - {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, - {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, - {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, - {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, - {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, - {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, - {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, - {"dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA}, - {"dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA}, - {"dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA}, - /* TLS 1.0: Exportable 56-bit Cipher Suites. */ - {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, - {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, - /* Ephemeral DH with RC4 bulk encryption */ - {"dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA}, - /* AES ciphers. */ - {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA}, - {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA}, - {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, - {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, - {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, - {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, - /* ECC ciphers. */ - {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, - {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, - {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, - {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, - {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, - {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, - {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, - {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, - {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, - {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, - {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, - {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, - {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, -#ifdef TLS_RSA_WITH_NULL_SHA256 - /* new HMAC-SHA256 cipher suites specified in RFC */ - {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256}, - {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256}, - {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256}, - {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, - {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, - {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, - {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, -#endif -#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256 - /* AES GCM cipher suites in RFC 5288 and RFC 5289 */ - {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256}, - {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256}, -#endif -#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - /* cipher suites using SHA384 */ - {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384}, - {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, - {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, -#endif -#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - /* chacha20-poly1305 cipher suites */ - {"ecdhe_rsa_chacha20_poly1305_sha_256", - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, - {"ecdhe_ecdsa_chacha20_poly1305_sha_256", - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, - {"dhe_rsa_chacha20_poly1305_sha_256", - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_AES_256_GCM_SHA384 - {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, - {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, - {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */ - {"dhe_dss_aes_128_sha_256", TLS_DHE_DSS_WITH_AES_128_CBC_SHA256}, - {"dhe_dss_aes_256_sha_256", TLS_DHE_DSS_WITH_AES_256_CBC_SHA256}, -#endif -#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - /* Camellia cipher suites in RFC 4132/5932. - Introduced in NSS release 3.12 */ - {"dhe_rsa_camellia_128_sha", TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_dss_camellia_128_sha", TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_rsa_camellia_256_sha", TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA}, - {"dhe_dss_camellia_256_sha", TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA}, - {"rsa_camellia_128_sha", TLS_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"rsa_camellia_256_sha", TLS_RSA_WITH_CAMELLIA_256_CBC_SHA}, -#endif -#ifdef TLS_RSA_WITH_SEED_CBC_SHA - /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */ - {"rsa_seed_sha", TLS_RSA_WITH_SEED_CBC_SHA}, -#endif -}; - -#if defined(WIN32) -static const char *pem_library = "nsspem.dll"; -static const char *trust_library = "nssckbi.dll"; -#elif defined(__APPLE__) -static const char *pem_library = "libnsspem.dylib"; -static const char *trust_library = "libnssckbi.dylib"; -#else -static const char *pem_library = "libnsspem.so"; -static const char *trust_library = "libnssckbi.so"; -#endif - -static SECMODModule *pem_module = NULL; -static SECMODModule *trust_module = NULL; - -/* NSPR I/O layer we use to detect blocking direction during SSL handshake */ -static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; -static PRIOMethods nspr_io_methods; - -static const char *nss_error_to_name(PRErrorCode code) -{ - const char *name = PR_ErrorToName(code); - if(name) - return name; - - return "unknown error"; -} - -static void nss_print_error_message(struct Curl_easy *data, PRUint32 err) -{ - failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); -} - -static char *nss_sslver_to_name(PRUint16 nssver) -{ - switch(nssver) { - case SSL_LIBRARY_VERSION_2: - return strdup("SSLv2"); - case SSL_LIBRARY_VERSION_3_0: - return strdup("SSLv3"); - case SSL_LIBRARY_VERSION_TLS_1_0: - return strdup("TLSv1.0"); -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - case SSL_LIBRARY_VERSION_TLS_1_1: - return strdup("TLSv1.1"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - case SSL_LIBRARY_VERSION_TLS_1_2: - return strdup("TLSv1.2"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - case SSL_LIBRARY_VERSION_TLS_1_3: - return strdup("TLSv1.3"); -#endif - default: - return curl_maprintf("0x%04x", nssver); - } -} - -/* the longest cipher name this supports */ -#define MAX_CIPHER_LENGTH 128 - -static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model, - const char *cipher_list) -{ - unsigned int i; - const char *cipher; - - /* use accessors to avoid dynamic linking issues after an update of NSS */ - const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); - const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers(); - if(!implemented_ciphers) - return SECFailure; - - /* First disable all ciphers. This uses a different max value in case - * NSS adds more ciphers later we don't want them available by - * accident - */ - for(i = 0; i < num_implemented_ciphers; i++) { - SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); - } - - cipher = cipher_list; - - while(cipher && cipher[0]) { - const char *end; - char name[MAX_CIPHER_LENGTH + 1]; - size_t len; - bool found = FALSE; - while((*cipher) && (ISBLANK(*cipher))) - ++cipher; - - end = strpbrk(cipher, ":, "); - if(end) - len = end - cipher; - else - len = strlen(cipher); - - if(len > MAX_CIPHER_LENGTH) { - failf(data, "Bad cipher list"); - return SECFailure; - } - else if(len) { - memcpy(name, cipher, len); - name[len] = 0; - - for(i = 0; i. - */ -static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name) -{ - PK11SlotInfo *slot; - PR_Lock(nss_findslot_lock); - slot = PK11_FindSlotByName(slot_name); - PR_Unlock(nss_findslot_lock); - return slot; -} - -/* wrap 'ptr' as list node and tail-insert into 'list' */ -static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr) -{ - struct ptr_list_wrap *wrap = malloc(sizeof(*wrap)); - if(!wrap) - return CURLE_OUT_OF_MEMORY; - - wrap->ptr = ptr; - Curl_llist_insert_next(list, list->tail, wrap, &wrap->node); - return CURLE_OK; -} - -/* Call PK11_CreateGenericObject() with the given obj_class and filename. If - * the call succeeds, append the object handle to the list of objects so that - * the object can be destroyed in nss_close(). */ -static CURLcode nss_create_object(struct ssl_connect_data *connssl, - CK_OBJECT_CLASS obj_class, - const char *filename, bool cacert) -{ - PK11SlotInfo *slot; - PK11GenericObject *obj; - CK_BBOOL cktrue = CK_TRUE; - CK_BBOOL ckfalse = CK_FALSE; - CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; - int attr_cnt = 0; - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - const int slot_id = (cacert) ? 0 : 1; - char *slot_name = aprintf("PEM Token #%d", slot_id); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(!slot_name) - return CURLE_OUT_OF_MEMORY; - - slot = nss_find_slot_by_name(slot_name); - free(slot_name); - if(!slot) - return result; - - PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); - PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); - PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, - (CK_ULONG)strlen(filename) + 1); - - if(CKO_CERTIFICATE == obj_class) { - CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); - PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); - } - - /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because - * PK11_DestroyGenericObject() does not release resources allocated by - * PK11_CreateGenericObject() early enough. */ - obj = -#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT - PK11_CreateManagedGenericObject -#else - PK11_CreateGenericObject -#endif - (slot, attrs, attr_cnt, PR_FALSE); - - PK11_FreeSlot(slot); - if(!obj) - return result; - - if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) { - PK11_DestroyGenericObject(obj); - return CURLE_OUT_OF_MEMORY; - } - - if(!cacert && CKO_CERTIFICATE == obj_class) - /* store reference to a client certificate */ - backend->obj_clicert = obj; - - return CURLE_OK; -} - -/* Destroy the NSS object whose handle is given by ptr. This function is - * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy - * NSS objects in nss_close() */ -static void nss_destroy_object(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr; - (void) user; - PK11_DestroyGenericObject(obj); - free(wrap); -} - -/* same as nss_destroy_object() but for CRL items */ -static void nss_destroy_crl_item(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - SECItem *crl_der = (SECItem *) wrap->ptr; - (void) user; - SECITEM_FreeItem(crl_der, PR_TRUE); - free(wrap); -} - -static CURLcode nss_load_cert(struct ssl_connect_data *ssl, - const char *filename, PRBool cacert) -{ - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - /* libnsspem.so leaks memory if the requested file does not exist. For more - * details, go to . */ - if(is_file(filename)) - result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); - - if(!result && !cacert) { - /* we have successfully loaded a client certificate */ - char *nickname = NULL; - char *n = strrchr(filename, '/'); - if(n) - n++; - - /* The following undocumented magic helps to avoid a SIGSEGV on call - * of PK11_ReadRawAttribute() from SelectClientCert() when using an - * immature version of libnsspem.so. For more details, go to - * . */ - nickname = aprintf("PEM Token #1:%s", n); - if(nickname) { - CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL); - if(cert) - CERT_DestroyCertificate(cert); - - free(nickname); - } - } - - return result; -} - -/* add given CRL to cache if it is not already there */ -static CURLcode nss_cache_crl(SECItem *crl_der) -{ - CERTCertDBHandle *db = CERT_GetDefaultCertDB(); - CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0); - if(crl) { - /* CRL already cached */ - SEC_DestroyCrl(crl); - SECITEM_FreeItem(crl_der, PR_TRUE); - return CURLE_OK; - } - - /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ - PR_Lock(nss_crllock); - - if(SECSuccess != CERT_CacheCRL(db, crl_der)) { - /* unable to cache CRL */ - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_SSL_CRL_BADFILE; - } - - /* store the CRL item so that we can free it in nss_cleanup() */ - if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { - if(SECSuccess == CERT_UncacheCRL(db, crl_der)) - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_OUT_OF_MEMORY; - } - - /* we need to clear session cache, so that the CRL could take effect */ - SSL_ClearSessionCache(); - PR_Unlock(nss_crllock); - return CURLE_OK; -} - -static CURLcode nss_load_crl(const char *crlfilename) -{ - PRFileDesc *infile; - PRFileInfo info; - SECItem filedata = { 0, NULL, 0 }; - SECItem *crl_der = NULL; - char *body; - - infile = PR_Open(crlfilename, PR_RDONLY, 0); - if(!infile) - return CURLE_SSL_CRL_BADFILE; - - if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) - goto fail; - - if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) - goto fail; - - if(info.size != PR_Read(infile, filedata.data, info.size)) - goto fail; - - crl_der = SECITEM_AllocItem(NULL, NULL, 0U); - if(!crl_der) - goto fail; - - /* place a trailing zero right after the visible data */ - body = (char *)filedata.data; - body[--filedata.len] = '\0'; - - body = strstr(body, "-----BEGIN"); - if(body) { - /* assume ASCII */ - char *trailer; - char *begin = PORT_Strchr(body, '\n'); - if(!begin) - begin = PORT_Strchr(body, '\r'); - if(!begin) - goto fail; - - trailer = strstr(++begin, "-----END"); - if(!trailer) - goto fail; - - /* retrieve DER from ASCII */ - *trailer = '\0'; - if(ATOB_ConvertAsciiToItem(crl_der, begin)) - goto fail; - - SECITEM_FreeItem(&filedata, PR_FALSE); - } - else - /* assume DER */ - *crl_der = filedata; - - PR_Close(infile); - return nss_cache_crl(crl_der); - -fail: - PR_Close(infile); - SECITEM_FreeItem(crl_der, PR_TRUE); - SECITEM_FreeItem(&filedata, PR_FALSE); - return CURLE_SSL_CRL_BADFILE; -} - -static CURLcode nss_load_key(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *key_file) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - PK11SlotInfo *slot, *tmp; - SECStatus status; - CURLcode result; - - (void)data; - result = nss_create_object(connssl, CKO_PRIVATE_KEY, key_file, FALSE); - if(result) { - PR_SetError(SEC_ERROR_BAD_KEY, 0); - return result; - } - - slot = nss_find_slot_by_name("PEM Token #1"); - if(!slot) - return CURLE_SSL_CERTPROBLEM; - - /* This will force the token to be seen as re-inserted */ - tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); - if(tmp) - PK11_FreeSlot(tmp); - if(!PK11_IsPresent(slot)) { - PK11_FreeSlot(slot); - return CURLE_SSL_CERTPROBLEM; - } - - status = PK11_Authenticate(slot, PR_TRUE, ssl_config->key_passwd); - PK11_FreeSlot(slot); - - return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; -} - -static int display_error(struct Curl_easy *data, PRInt32 err, - const char *filename) -{ - switch(err) { - case SEC_ERROR_BAD_PASSWORD: - failf(data, "Unable to load client key: Incorrect password"); - return 1; - case SEC_ERROR_UNKNOWN_CERT: - failf(data, "Unable to load certificate %s", filename); - return 1; - default: - break; - } - return 0; /* The caller will print a generic error */ -} - -static CURLcode cert_stuff(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *cert_file, char *key_file) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - - if(cert_file) { - result = nss_load_cert(connssl, cert_file, PR_FALSE); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, cert_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client cert: %d (%s)", err, err_name); - } - - return result; - } - } - - if(key_file || (is_file(cert_file))) { - if(key_file) - result = nss_load_key(cf, data, key_file); - else - /* In case the cert file also has the key */ - result = nss_load_key(cf, data, cert_file); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, key_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client key: %d (%s)", err, err_name); - } - - return result; - } - } - - return CURLE_OK; -} - -static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - (void)slot; /* unused */ - - if(retry || !arg) - return NULL; - else - return (char *)PORT_Strdup((char *)arg); -} - -/* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ -static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, - PRBool isServer) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct Curl_easy *data = connssl->backend->data; - - DEBUGASSERT(data); -#ifdef SSL_ENABLE_OCSP_STAPLING - if(conn_config->verifystatus) { - SECStatus cacheResult; - - const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); - if(!csa) { - failf(data, "Invalid OCSP response"); - return SECFailure; - } - - if(csa->len == 0) { - failf(data, "No OCSP response received"); - return SECFailure; - } - - cacheResult = CERT_CacheOCSPResponseFromSideChannel( - CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), - PR_Now(), &csa->items[0], arg - ); - - if(cacheResult != SECSuccess) { - failf(data, "Invalid OCSP response"); - return cacheResult; - } - } -#endif - - if(!conn_config->verifypeer) { - infof(data, "skipping SSL peer certificate verification"); - return SECSuccess; - } - - return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); -} - -/** - * Inform the application that the handshake is complete. - */ -static void HandshakeCallback(PRFileDesc *sock, void *arg) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->backend->data; - unsigned int buflenmax = 50; - unsigned char buf[50]; - unsigned int buflen; - SSLNextProtoState state; - - DEBUGASSERT(data); - if(!connssl->alpn) { - return; - } - - if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { - - switch(state) { -#if NSSVERNUM >= 0x031a00 /* 3.26.0 */ - /* used by NSS internally to implement 0-RTT */ - case SSL_NEXT_PROTO_EARLY_VALUE: - /* fall through! */ -#endif - case SSL_NEXT_PROTO_NO_SUPPORT: - case SSL_NEXT_PROTO_NO_OVERLAP: - Curl_alpn_set_negotiated(cf, data, NULL, 0); - return; -#ifdef SSL_ENABLE_ALPN - case SSL_NEXT_PROTO_SELECTED: - Curl_alpn_set_negotiated(cf, data, buf, buflen); - break; -#endif - default: - /* ignore SSL_NEXT_PROTO_NEGOTIATED */ - break; - } - - } -} - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ -static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, - PRBool *canFalseStart) -{ - struct Curl_easy *data = (struct Curl_easy *)client_data; - - SSLChannelInfo channelInfo; - SSLCipherSuiteInfo cipherInfo; - - SECStatus rv; - PRBool negotiatedExtension; - - *canFalseStart = PR_FALSE; - - if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) - return SECFailure; - - if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, - sizeof(cipherInfo)) != SECSuccess) - return SECFailure; - - /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for - * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 - */ - if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) - goto end; - - /* Only allow ECDHE key exchange algorithm. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ - if(cipherInfo.keaType != ssl_kea_ecdh) - goto end; - - /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC - * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt - * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ - if(cipherInfo.symCipher != ssl_calg_aes_gcm) - goto end; - - /* Enforce ALPN to do False Start, as an indicator of server - compatibility. */ - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, - &negotiatedExtension); - if(rv != SECSuccess || !negotiatedExtension) { - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, - &negotiatedExtension); - } - - if(rv != SECSuccess || !negotiatedExtension) - goto end; - - *canFalseStart = PR_TRUE; - - infof(data, "Trying TLS False Start"); - -end: - return SECSuccess; -} -#endif - -static void display_cert_info(struct Curl_easy *data, - CERTCertificate *cert) -{ - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; - - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(data, "subject: %s", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " start date: %s", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " expire date: %s", timeString); - infof(data, " common name: %s", common_name); - infof(data, " issuer: %s", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); -} - -/* A number of certs that will never occur in a real server handshake */ -#define TOO_MANY_CERTS 300 - -static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock) -{ - CURLcode result = CURLE_OK; - SSLChannelInfo channel; - SSLCipherSuiteInfo suite; - CERTCertificate *cert; - CERTCertificate *cert2; - CERTCertificate *cert3; - PRTime now; - - if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) == - SECSuccess && channel.length == sizeof(channel) && - channel.cipherSuite) { - if(SSL_GetCipherSuiteInfo(channel.cipherSuite, - &suite, sizeof(suite)) == SECSuccess) { - infof(data, "SSL connection using %s", suite.cipherSuiteName); - } - } - - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - - if(!data->set.ssl.certinfo) { - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - else { - /* Count certificates in chain. */ - int i = 1; - now = PR_Now(); - if(!cert->isRoot) { - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - while(cert2) { - i++; - if(i >= TOO_MANY_CERTS) { - CERT_DestroyCertificate(cert2); - failf(data, "certificate loop"); - return CURLE_SSL_CERTPROBLEM; - } - if(cert2->isRoot) { - CERT_DestroyCertificate(cert2); - break; - } - cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); - CERT_DestroyCertificate(cert2); - cert2 = cert3; - } - } - - result = Curl_ssl_init_certinfo(data, i); - if(!result) { - for(i = 0; cert; cert = cert2) { - result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data, - (char *)cert->derCert.data + - cert->derCert.len); - if(result) - break; - - if(cert->isRoot) { - CERT_DestroyCertificate(cert); - break; - } - - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - CERT_DestroyCertificate(cert); - } - } - } - } - - return result; -} - -static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->backend->data; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config; - PRErrorCode err = PR_GetError(); - CERTCertificate *cert; - - DEBUGASSERT(data); - ssl_config = Curl_ssl_cf_get_config(cf, data); - /* remember the cert verification result */ - ssl_config->certverifyresult = err; - - if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn_config->verifyhost) - /* we are asked not to verify the host name */ - return SECSuccess; - - /* print only info about the cert, the error is printed off the callback */ - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - - return SECFailure; -} - -/** - * - * Check that the Peer certificate's issuer certificate matches the one found - * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the - * issuer check, so we provide comments that mimic the OpenSSL - * X509_check_issued function (in x509v3/v3_purp.c) - */ -static SECStatus check_issuer_cert(PRFileDesc *sock, - char *issuer_nickname) -{ - CERTCertificate *cert, *cert_issuer, *issuer; - SECStatus res = SECSuccess; - void *proto_win = NULL; - - cert = SSL_PeerCertificate(sock); - cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); - - proto_win = SSL_RevealPinArg(sock); - issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); - - if((!cert_issuer) || (!issuer)) - res = SECFailure; - else if(SECITEM_CompareItem(&cert_issuer->derCert, - &issuer->derCert) != SECEqual) - res = SECFailure; - - CERT_DestroyCertificate(cert); - CERT_DestroyCertificate(issuer); - CERT_DestroyCertificate(cert_issuer); - return res; -} - -static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, - const char *pinnedpubkey) -{ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - CERTCertificate *cert; - - DEBUGASSERT(backend); - data = backend->data; - - if(!pinnedpubkey) - /* no pinned public key specified */ - return CURLE_OK; - - /* get peer certificate */ - cert = SSL_PeerCertificate(backend->handle); - if(cert) { - /* extract public key from peer certificate */ - SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); - if(pubkey) { - /* encode the public key as DER */ - SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); - if(cert_der) { - /* compare the public key with the pinned public key */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data, - cert_der->len); - SECITEM_FreeItem(cert_der, PR_TRUE); - } - SECKEY_DestroyPublicKey(pubkey); - } - CERT_DestroyCertificate(cert); - } - - /* report the resulting status */ - switch(result) { - case CURLE_OK: - infof(data, "pinned public key verified successfully"); - break; - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: - failf(data, "failed to verify pinned public key"); - break; - default: - /* OOM, etc. */ - break; - } - - return result; -} - -/** - * - * Callback to pick the SSL client certificate. - */ -static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, - struct CERTDistNamesStr *caNames, - struct CERTCertificateStr **pRetCert, - struct SECKEYPrivateKeyStr **pRetKey) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - const char *nickname = NULL; - static const char pem_slotname[] = "PEM Token #1"; - - DEBUGASSERT(backend); - - data = backend->data; - nickname = backend->client_nickname; - - if(backend->obj_clicert) { - /* use the cert/key provided by PEM reader */ - SECItem cert_der = { 0, NULL, 0 }; - void *proto_win = SSL_RevealPinArg(sock); - struct CERTCertificateStr *cert; - struct SECKEYPrivateKeyStr *key; - - PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); - if(!slot) { - failf(data, "NSS: PK11 slot not found: %s", pem_slotname); - return SECFailure; - } - - if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE, - &cert_der) != SECSuccess) { - failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); - PK11_FreeSlot(slot); - return SECFailure; - } - - cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); - SECITEM_FreeItem(&cert_der, PR_FALSE); - if(!cert) { - failf(data, "NSS: client certificate from file not found"); - PK11_FreeSlot(slot); - return SECFailure; - } - - key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); - PK11_FreeSlot(slot); - if(!key) { - failf(data, "NSS: private key from file not found"); - CERT_DestroyCertificate(cert); - return SECFailure; - } - - infof(data, "NSS: client certificate from file"); - display_cert_info(data, cert); - - *pRetCert = cert; - *pRetKey = key; - return SECSuccess; - } - - /* use the default NSS hook */ - if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, - pRetCert, pRetKey) - || !*pRetCert) { - - if(!nickname) - failf(data, "NSS: client certificate not found (nickname not " - "specified)"); - else - failf(data, "NSS: client certificate not found: %s", nickname); - - return SECFailure; - } - - /* get certificate nickname if any */ - nickname = (*pRetCert)->nickname; - if(!nickname) - nickname = "[unknown]"; - - if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { - failf(data, "NSS: refusing previously loaded certificate from file: %s", - nickname); - return SECFailure; - } - - if(!*pRetKey) { - failf(data, "NSS: private key not found for certificate: %s", nickname); - return SECFailure; - } - - infof(data, "NSS: using client certificate: %s", nickname); - display_cert_info(data, *pRetCert); - return SECSuccess; -} - -/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ -static void nss_update_connecting_state(ssl_connect_state state, void *secret) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret; - if(PR_GetError() != PR_WOULD_BLOCK_ERROR) - /* an unrelated error is passing by */ - return; - - switch(connssl->connecting_state) { - case ssl_connect_2: - case ssl_connect_2_reading: - case ssl_connect_2_writing: - break; - default: - /* we are not called from an SSL handshake */ - return; - } - - /* update the state accordingly */ - connssl->connecting_state = state; -} - -/* recv() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRRecvFN recv_fn = fd->lower->methods->recv; - const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_reading, fd->secret); - return rv; -} - -/* send() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRSendFN send_fn = fd->lower->methods->send; - const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_writing, fd->secret); - return rv; -} - -/* close() wrapper to avoid assertion failure due to fd->secret != NULL */ -static PRStatus nspr_io_close(PRFileDesc *fd) -{ - const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close; - fd->secret = NULL; - return close_fn(fd); -} - -/* load a PKCS #11 module */ -static CURLcode nss_load_module(SECMODModule **pmod, const char *library, - const char *name) -{ - char *config_string; - SECMODModule *module = *pmod; - if(module) - /* already loaded */ - return CURLE_OK; - - config_string = aprintf("library=%s name=%s", library, name); - if(!config_string) - return CURLE_OUT_OF_MEMORY; - - module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); - free(config_string); - - if(module && module->loaded) { - /* loaded successfully */ - *pmod = module; - return CURLE_OK; - } - - if(module) - SECMOD_DestroyModule(module); - return CURLE_FAILED_INIT; -} - -/* unload a PKCS #11 module */ -static void nss_unload_module(SECMODModule **pmod) -{ - SECMODModule *module = *pmod; - if(!module) - /* not loaded */ - return; - - if(SECMOD_UnloadUserModule(module) != SECSuccess) - /* unload failed */ - return; - - SECMOD_DestroyModule(module); - *pmod = NULL; -} - -/* data might be NULL */ -static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) -{ - NSSInitParameters initparams; - PRErrorCode err; - const char *err_name; - - if(nss_context) - return CURLE_OK; - - memset((void *) &initparams, '\0', sizeof(initparams)); - initparams.length = sizeof(initparams); - - if(cert_dir) { - char *certpath = aprintf("sql:%s", cert_dir); - if(!certpath) - return CURLE_OUT_OF_MEMORY; - - infof(data, "Initializing NSS with certpath: %s", certpath); - nss_context = NSS_InitContext(certpath, "", "", "", &initparams, - NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); - free(certpath); - - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name); - } - - infof(data, "Initializing NSS with certpath: none"); - nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY - | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - failf(data, "Unable to initialize NSS: %d (%s)", err, err_name); - return CURLE_SSL_CACERT_BADFILE; -} - -/* data might be NULL */ -static CURLcode nss_setup(struct Curl_easy *data) -{ - char *cert_dir; - struct_stat st; - CURLcode result; - - if(initialized) - return CURLE_OK; - - /* list of all CRL items we need to destroy in nss_cleanup() */ - Curl_llist_init(&nss_crl_list, nss_destroy_crl_item); - - /* First we check if $SSL_DIR points to a valid dir */ - cert_dir = getenv("SSL_DIR"); - if(cert_dir) { - if((stat(cert_dir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - cert_dir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!cert_dir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - cert_dir = (char *)SSL_DIR; - } - } - - if(nspr_io_identity == PR_INVALID_IO_LAYER) { - /* allocate an identity for our own NSPR I/O layer */ - nspr_io_identity = PR_GetUniqueIdentity("libcurl"); - if(nspr_io_identity == PR_INVALID_IO_LAYER) - return CURLE_OUT_OF_MEMORY; - - /* the default methods just call down to the lower I/O layer */ - memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(), - sizeof(nspr_io_methods)); - - /* override certain methods in the table by our wrappers */ - nspr_io_methods.recv = nspr_io_recv; - nspr_io_methods.send = nspr_io_send; - nspr_io_methods.close = nspr_io_close; - } - - result = nss_init_core(data, cert_dir); - if(result) - return result; - - if(!any_cipher_enabled()) - NSS_SetDomesticPolicy(); - - initialized = 1; - - return CURLE_OK; -} - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -static int nss_init(void) -{ - /* curl_global_init() is not thread-safe so this test is ok */ - if(!nss_initlock) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); - nss_initlock = PR_NewLock(); - nss_crllock = PR_NewLock(); - nss_findslot_lock = PR_NewLock(); - nss_trustload_lock = PR_NewLock(); - } - - /* We will actually initialize NSS later */ - - return 1; -} - -/* data might be NULL */ -CURLcode Curl_nss_force_init(struct Curl_easy *data) -{ - CURLcode result; - if(!nss_initlock) { - if(data) - failf(data, "unable to initialize NSS, curl_global_init() should have " - "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); - return CURLE_FAILED_INIT; - } - - PR_Lock(nss_initlock); - result = nss_setup(data); - PR_Unlock(nss_initlock); - - return result; -} - -/* Global cleanup */ -static void nss_cleanup(void) -{ - /* This function isn't required to be threadsafe and this is only done - * as a safety feature. - */ - PR_Lock(nss_initlock); - if(initialized) { - /* Free references to client certificates held in the SSL session cache. - * Omitting this hampers destruction of the security module owning - * the certificates. */ - SSL_ClearSessionCache(); - - nss_unload_module(&pem_module); - nss_unload_module(&trust_module); - NSS_ShutdownContext(nss_context); - nss_context = NULL; - } - - /* destroy all CRL items */ - Curl_llist_destroy(&nss_crl_list, NULL); - - PR_Unlock(nss_initlock); - - PR_DestroyLock(nss_initlock); - PR_DestroyLock(nss_crllock); - PR_DestroyLock(nss_findslot_lock); - PR_DestroyLock(nss_trustload_lock); - nss_initlock = NULL; - - initialized = 0; -} - -static void close_one(struct ssl_connect_data *connssl) -{ - /* before the cleanup, check whether we are using a client certificate */ - struct ssl_backend_data *backend = connssl->backend; - bool client_cert = true; - - DEBUGASSERT(backend); - - client_cert = (backend->client_nickname != NULL) - || (backend->obj_clicert != NULL); - - if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0, - PR_INTERVAL_NO_WAIT); - } - - free(backend->client_nickname); - backend->client_nickname = NULL; - - /* destroy all NSS objects in order to avoid failure of NSS shutdown */ - Curl_llist_destroy(&backend->obj_list, NULL); - backend->obj_clicert = NULL; - - if(backend->handle) { - if(client_cert) - /* A server might require different authentication based on the - * particular path being requested by the client. To support this - * scenario, we must ensure that a connection will never reuse the - * authentication data from a previous connection. */ - SSL_InvalidateSession(backend->handle); - - PR_Close(backend->handle); - backend->handle = NULL; - } -} - -/* - * This function is called when an SSL connection is closed. - */ -static void nss_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - (void)data; - DEBUGASSERT(backend); - - if(backend->handle) { - /* NSS closes the socket we previously handed to it, so we must mark it - as closed to avoid double close */ - fake_sclose(cf->conn->sock[cf->sockindex]); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - - close_one(connssl); -} - -/* return true if NSS can provide error code (and possibly msg) for the - error */ -static bool is_nss_error(CURLcode err) -{ - switch(err) { - case CURLE_PEER_FAILED_VERIFICATION: - case CURLE_SSL_CERTPROBLEM: - case CURLE_SSL_CONNECT_ERROR: - case CURLE_SSL_ISSUER_ERROR: - return true; - - default: - return false; - } -} - -/* return true if the given error code is related to a client certificate */ -static bool is_cc_error(PRInt32 err) -{ - switch(err) { - case SSL_ERROR_BAD_CERT_ALERT: - case SSL_ERROR_EXPIRED_CERT_ALERT: - case SSL_ERROR_REVOKED_CERT_ALERT: - return true; - - default: - return false; - } -} - -static CURLcode nss_load_ca_certificates(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const char *cafile = conn_config->CAfile; - const char *capath = conn_config->CApath; - bool use_trust_module; - CURLcode result = CURLE_OK; - - /* treat empty string as unset */ - if(cafile && !cafile[0]) - cafile = NULL; - if(capath && !capath[0]) - capath = NULL; - - infof(data, " CAfile: %s", cafile ? cafile : "none"); - infof(data, " CApath: %s", capath ? capath : "none"); - - /* load libnssckbi.so if no other trust roots were specified */ - use_trust_module = !cafile && !capath; - - PR_Lock(nss_trustload_lock); - if(use_trust_module && !trust_module) { - /* libnssckbi.so needed but not yet loaded --> load it! */ - result = nss_load_module(&trust_module, trust_library, "trust"); - infof(data, "%s %s", (result) ? "failed to load" : "loaded", - trust_library); - if(result == CURLE_FAILED_INIT) - /* If libnssckbi.so is not available (or fails to load), one can still - use CA certificates stored in NSS database. Ignore the failure. */ - result = CURLE_OK; - } - else if(!use_trust_module && trust_module) { - /* libnssckbi.so not needed but already loaded --> unload it! */ - infof(data, "unloading %s", trust_library); - nss_unload_module(&trust_module); - } - PR_Unlock(nss_trustload_lock); - - if(cafile) - result = nss_load_cert(connssl, cafile, PR_TRUE); - - if(result) - return result; - - if(capath) { - struct_stat st; - if(stat(capath, &st) == -1) - return CURLE_SSL_CACERT_BADFILE; - - if(S_ISDIR(st.st_mode)) { - PRDirEntry *entry; - PRDir *dir = PR_OpenDir(capath); - if(!dir) - return CURLE_SSL_CACERT_BADFILE; - - while((entry = - PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) { - char *fullpath = aprintf("%s/%s", capath, entry->name); - if(!fullpath) { - PR_CloseDir(dir); - return CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK != nss_load_cert(connssl, fullpath, PR_TRUE)) - /* This is purposefully tolerant of errors so non-PEM files can - * be in the same directory */ - infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath); - - free(fullpath); - } - - PR_CloseDir(dir); - } - else - infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath); - } - - return CURLE_OK; -} - -static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version) -{ - switch(version) { - case CURL_SSLVERSION_SSLv2: - *nssver = SSL_LIBRARY_VERSION_2; - return CURLE_OK; - - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; - - case CURL_SSLVERSION_TLSv1_0: - *nssver = SSL_LIBRARY_VERSION_TLS_1_0; - return CURLE_OK; - - case CURL_SSLVERSION_TLSv1_1: -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - *nssver = SSL_LIBRARY_VERSION_TLS_1_1; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_2: -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - *nssver = SSL_LIBRARY_VERSION_TLS_1_2; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_3: -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - *nssver = SSL_LIBRARY_VERSION_TLS_1_3; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - default: - return CURLE_SSL_CONNECT_ERROR; - } -} - -static CURLcode nss_init_sslver(SSLVersionRange *sslver, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - CURLcode result; - const long min = conn_config->version; - const long max = conn_config->version_max; - SSLVersionRange vrange; - - switch(min) { - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_DEFAULT: - /* Bump our minimum TLS version if NSS has stricter requirements. */ - if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) - return CURLE_SSL_CONNECT_ERROR; - if(sslver->min < vrange.min) - sslver->min = vrange.min; - break; - default: - result = nss_sslver_from_curl(&sslver->min, min); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - switch(max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - break; - default: - result = nss_sslver_from_curl(&sslver->max, max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - return CURLE_OK; -} - -static CURLcode nss_fail_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode curlerr) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(is_nss_error(curlerr)) { - /* read NSPR error code */ - PRErrorCode err = PR_GetError(); - if(is_cc_error(err)) - curlerr = CURLE_SSL_CERTPROBLEM; - - /* print the error number and error string */ - infof(data, "NSS error %d (%s)", err, nss_error_to_name(err)); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - } - - /* cleanup on connection failure */ - Curl_llist_destroy(&backend->obj_list, NULL); - - return curlerr; -} - -/* Switch the SSL socket into blocking or non-blocking mode. */ -static CURLcode nss_set_blocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking) -{ - struct ssl_connect_data *connssl = cf->ctx; - PRSocketOptionData sock_opt; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - sock_opt.option = PR_SockOpt_Nonblocking; - sock_opt.value.non_blocking = !blocking; - - if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS) - return nss_fail_connect(cf, data, CURLE_SSL_CONNECT_ERROR); - - return CURLE_OK; -} - -static CURLcode nss_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - PRFileDesc *model = NULL; - PRFileDesc *nspr_io = NULL; - PRFileDesc *nspr_io_stub = NULL; - PRBool ssl_no_cache; - PRBool ssl_cbc_random_iv; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - CURLcode result; - bool second_layer = FALSE; - SSLVersionRange sslver_supported; - SSLVersionRange sslver = { - SSL_LIBRARY_VERSION_TLS_1_0, /* min */ -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - SSL_LIBRARY_VERSION_TLS_1_3 /* max */ -#elif defined SSL_LIBRARY_VERSION_TLS_1_2 - SSL_LIBRARY_VERSION_TLS_1_2 -#elif defined SSL_LIBRARY_VERSION_TLS_1_1 - SSL_LIBRARY_VERSION_TLS_1_1 -#else - SSL_LIBRARY_VERSION_TLS_1_0 -#endif - }; - const char *hostname = connssl->hostname; - char *snihost; - - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGASSERT(backend); - - backend->data = data; - - /* list of all NSS objects we need to destroy in nss_do_close() */ - Curl_llist_init(&backend->obj_list, nss_destroy_object); - - PR_Lock(nss_initlock); - result = nss_setup(data); - if(result) { - PR_Unlock(nss_initlock); - goto error; - } - - PK11_SetPasswordFunc(nss_get_password); - - result = nss_load_module(&pem_module, pem_library, "PEM"); - PR_Unlock(nss_initlock); - if(result == CURLE_FAILED_INIT) - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.", pem_library); - else if(result) - goto error; - - result = CURLE_SSL_CONNECT_ERROR; - - model = PR_NewTCPSocket(); - if(!model) - goto error; - model = SSL_ImportFD(NULL, model); - - if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) - goto error; - - /* do not use SSL cache if disabled or we are not going to verify peer */ - ssl_no_cache = (ssl_config->primary.sessionid - && conn_config->verifypeer) ? PR_FALSE : PR_TRUE; - if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) - goto error; - - /* enable/disable the requested SSL version(s) */ - if(nss_init_sslver(&sslver, cf, data) != CURLE_OK) - goto error; - if(SSL_VersionRangeGetSupported(ssl_variant_stream, - &sslver_supported) != SECSuccess) - goto error; - if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { - char *sslver_req_str, *sslver_supp_str; - sslver_req_str = nss_sslver_to_name(sslver.max); - sslver_supp_str = nss_sslver_to_name(sslver_supported.max); - if(sslver_req_str && sslver_supp_str) - infof(data, "Falling back from %s to max supported SSL version (%s)", - sslver_req_str, sslver_supp_str); - free(sslver_req_str); - free(sslver_supp_str); - sslver.max = sslver_supported.max; - } - if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) - goto error; - - ssl_cbc_random_iv = !ssl_config->enable_beast; -#ifdef SSL_CBC_RANDOM_IV - /* unless the user explicitly asks to allow the protocol vulnerability, we - use the work-around */ - if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) - infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", - ssl_cbc_random_iv); -#else - if(ssl_cbc_random_iv) - infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); -#endif - - if(conn_config->cipher_list) { - if(set_ciphers(data, model, conn_config->cipher_list) != SECSuccess) { - result = CURLE_SSL_CIPHER; - goto error; - } - } - - if(!conn_config->verifypeer && conn_config->verifyhost) - infof(data, "WARNING: ignoring value of ssl.verifyhost"); - - /* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ - if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, cf) != SECSuccess) - goto error; - - /* not checked yet */ - ssl_config->certverifyresult = 0; - - if(SSL_BadCertHook(model, BadCertHandler, cf) != SECSuccess) - goto error; - - if(SSL_HandshakeCallback(model, HandshakeCallback, cf) != SECSuccess) - goto error; - - { - const CURLcode rv = nss_load_ca_certificates(cf, data); - if((rv == CURLE_SSL_CACERT_BADFILE) && !conn_config->verifypeer) - /* not a fatal error because we are not going to verify the peer */ - infof(data, "WARNING: CA certificates failed to load"); - else if(rv) { - result = rv; - goto error; - } - } - - if(ssl_config->primary.CRLfile) { - const CURLcode rv = nss_load_crl(ssl_config->primary.CRLfile); - if(rv) { - result = rv; - goto error; - } - infof(data, " CRLfile: %s", ssl_config->primary.CRLfile); - } - - if(ssl_config->primary.clientcert) { - char *nickname = dup_nickname(data, ssl_config->primary.clientcert); - if(nickname) { - /* we are not going to use libnsspem.so to read the client cert */ - backend->obj_clicert = NULL; - } - else { - CURLcode rv = cert_stuff(cf, data, - ssl_config->primary.clientcert, - ssl_config->key); - if(rv) { - /* failf() is already done in cert_stuff() */ - result = rv; - goto error; - } - } - - /* store the nickname for SelectClientCert() called during handshake */ - backend->client_nickname = nickname; - } - else - backend->client_nickname = NULL; - - if(SSL_GetClientAuthDataHook(model, SelectClientCert, - (void *)connssl) != SECSuccess) { - result = CURLE_SSL_CERTPROBLEM; - goto error; - } - - /* Is there an SSL filter "in front" of us or are we writing directly - * to the socket? */ - if(connssl_next) { - /* The filter should be connected by now, with full handshake */ - DEBUGASSERT(connssl_next->backend->handle); - DEBUGASSERT(ssl_connection_complete == connssl_next->state); - /* We tell our NSS instance to use do IO with the 'next' NSS - * instance. This NSS instance will take ownership of the next - * one, including its destruction. We therefore need to `disown` - * the next filter's handle, once import succeeds. */ - nspr_io = connssl_next->backend->handle; - second_layer = TRUE; - } - else { - /* wrap OS file descriptor by NSPR's file descriptor abstraction */ - nspr_io = PR_ImportTCPSocket(sockfd); - if(!nspr_io) - goto error; - } - - /* create our own NSPR I/O layer */ - nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); - if(!nspr_io_stub) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - /* make the per-connection data accessible from NSPR I/O callbacks */ - nspr_io_stub->secret = (void *)connssl; - - /* push our new layer to the NSPR I/O stack */ - if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { - if(!second_layer) - PR_Close(nspr_io); - PR_Close(nspr_io_stub); - goto error; - } - - /* import our model socket onto the current I/O stack */ - backend->handle = SSL_ImportFD(model, nspr_io); - if(!backend->handle) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - PR_Close(model); /* We don't need this any more */ - model = NULL; - if(connssl_next) /* steal the NSS handle we just imported successfully */ - connssl_next->backend->handle = NULL; - - /* This is the password associated with the cert that we're using */ - if(ssl_config->key_passwd) { - SSL_SetPKCS11PinArg(backend->handle, ssl_config->key_passwd); - } - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(conn_config->verifystatus) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) - != SECSuccess) - goto error; - } -#endif - -#ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, - connssl->alpn ? PR_TRUE : PR_FALSE) - != SECSuccess) - goto error; -#endif - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - if(data->set.ssl.falsestart) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) - != SECSuccess) - goto error; - - if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, - data) != SECSuccess) - goto error; - } -#endif - -#if defined(SSL_ENABLE_ALPN) - if(connssl->alpn) { - struct alpn_proto_buf proto; - - result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); - if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len) - != SECSuccess) { - failf(data, "Error setting ALPN"); - goto error; - } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); - } -#endif - - - /* Force handshake on next I/O */ - if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) - != SECSuccess) - goto error; - - /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, snihost) != SECSuccess) - goto error; - - /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) - goto error; - - return CURLE_OK; - -error: - if(model) - PR_Close(model); - - return nss_fail_connect(cf, data, result); -} - -static CURLcode nss_do_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode result = CURLE_SSL_CONNECT_ERROR; - PRUint32 timeout; - - /* check timeout situation */ - const timediff_t time_left = Curl_timeleft(data, NULL, TRUE); - if(time_left < 0) { - failf(data, "timed out before SSL handshake"); - result = CURLE_OPERATION_TIMEDOUT; - goto error; - } - - DEBUGASSERT(backend); - - /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32) time_left); - if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { - if(PR_GetError() == PR_WOULD_BLOCK_ERROR) - /* blocking direction is updated by nss_update_connecting_state() */ - return CURLE_AGAIN; - else if(ssl_config->certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) - result = CURLE_PEER_FAILED_VERIFICATION; - else if(ssl_config->certverifyresult) - result = CURLE_PEER_FAILED_VERIFICATION; - goto error; - } - - result = display_conn_info(data, backend->handle); - if(result) - goto error; - - if(conn_config->issuercert) { - SECStatus ret = SECFailure; - char *nickname = dup_nickname(data, conn_config->issuercert); - if(nickname) { - /* we support only nicknames in case of issuercert for now */ - ret = check_issuer_cert(backend->handle, nickname); - free(nickname); - } - - if(SECFailure == ret) { - infof(data, "SSL certificate issuer check failed"); - result = CURLE_SSL_ISSUER_ERROR; - goto error; - } - else { - infof(data, "SSL certificate issuer check ok"); - } - } - - result = cmp_peer_pubkey(connssl, Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) - /* status already printed */ - goto error; - - return CURLE_OK; - -error: - return nss_fail_connect(cf, data, result); -} - -static CURLcode nss_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - const bool blocking = (done == NULL); - CURLcode result; - - if(connssl->state == ssl_connection_complete) { - if(!blocking) - *done = TRUE; - return CURLE_OK; - } - - if(connssl->connecting_state == ssl_connect_1) { - result = nss_setup_connect(cf, data); - if(result) - /* we do not expect CURLE_AGAIN from nss_setup_connect() */ - return result; - - connssl->connecting_state = ssl_connect_2; - } - - /* enable/disable blocking mode before handshake */ - result = nss_set_blocking(cf, data, blocking); - if(result) - return result; - - result = nss_do_connect(cf, data); - switch(result) { - case CURLE_OK: - break; - case CURLE_AGAIN: - /* CURLE_AGAIN in non-blocking mode is not an error */ - if(!blocking) - return CURLE_OK; - else - return result; - default: - return result; - } - - if(blocking) { - /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ - result = nss_set_blocking(cf, data, /* blocking */ FALSE); - if(result) - return result; - } - else - /* signal completed SSL handshake */ - *done = TRUE; - - connssl->state = ssl_connection_complete; - - /* ssl_connect_done is never used outside, go back to the initial state */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode nss_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - return nss_connect_common(cf, data, /* blocking */ NULL); -} - -static CURLcode nss_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - return nss_connect_common(cf, data, done); -} - -static ssize_t nss_send(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - ssize_t rc; - - (void)data; - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); - if(rc < 0) { - PRInt32 err = PR_GetError(); - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL write: error %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_SEND_ERROR; - } - - return -1; - } - - return rc; /* number of bytes */ -} - -static bool -nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - PRFileDesc *fd = connssl->backend->handle->lower; - char buf; - - (void) data; - - /* Returns true in case of error to force reading. */ - return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0; -} - -static ssize_t nss_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - ssize_t nread; - - (void)data; - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - nread = PR_Recv(backend->handle, buf, (int)buffersize, 0, - PR_INTERVAL_NO_WAIT); - if(nread < 0) { - /* failed SSL read */ - PRInt32 err = PR_GetError(); - - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL read: errno %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_RECV_ERROR; - } - - return -1; - } - - return nread; -} - -static size_t nss_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion()); -} - -/* data might be NULL */ -static int Curl_nss_seed(struct Curl_easy *data) -{ - /* make sure that NSS is initialized */ - return !!Curl_nss_force_init(data); -} - -/* data might be NULL */ -static CURLcode nss_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) -{ - Curl_nss_seed(data); /* Initiate the seed if not already done */ - - if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) - /* signal a failure */ - return CURLE_FAILED_INIT; - - return CURLE_OK; -} - -static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); - unsigned int SHA256out; - - if(!SHA256pw) - return CURLE_NOT_BUILT_IN; - - PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); - PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); - PK11_DestroyContext(SHA256pw, PR_TRUE); - - return CURLE_OK; -} - -static bool nss_cert_status_request(void) -{ -#ifdef SSL_ENABLE_OCSP_STAPLING - return TRUE; -#else - return FALSE; -#endif -} - -static bool nss_false_start(void) -{ -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - return TRUE; -#else - return FALSE; -#endif -} - -static void *nss_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->handle; -} - -static bool nss_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - - if(!connssl->backend->data) - connssl->backend->data = data; - return TRUE; -} - -static void nss_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - - if(connssl->backend->data == data) - connssl->backend->data = NULL; -} - -const struct Curl_ssl Curl_ssl_nss = { - { CURLSSLBACKEND_NSS, "nss" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - nss_init, /* init */ - nss_cleanup, /* cleanup */ - nss_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - /* NSS has no shutdown function provided and thus always fail */ - Curl_none_shutdown, /* shutdown */ - nss_data_pending, /* data_pending */ - nss_random, /* random */ - nss_cert_status_request, /* cert_status_request */ - nss_connect, /* connect */ - nss_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ - nss_get_internals, /* get_internals */ - nss_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* NSS has its own session ID cache */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - nss_false_start, /* false_start */ - nss_sha256sum, /* sha256sum */ - nss_attach_data, /* associate_connection */ - nss_detach_data, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - nss_recv, /* recv decrypted data */ - nss_send, /* send data to encrypt */ -}; - -#endif /* USE_NSS */ diff --git a/vendor/curl/lib/vtls/openssl.c b/vendor/curl/lib/vtls/openssl.c index 6543fb19a2..a12e712b16 100644 --- a/vendor/curl/lib/vtls/openssl.c +++ b/vendor/curl/lib/vtls/openssl.c @@ -190,11 +190,12 @@ * 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: unsupported in at least 2.7.2 (explicitly check for it since it - * lies and pretends to be OpenSSL 2.0.0). + * LibreSSL: supported since 3.5.0 (released 2022-02-24) */ #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3050000fL) || \ defined(OPENSSL_IS_BORINGSSL) #define HAVE_KEYLOG_CALLBACK #endif @@ -202,11 +203,13 @@ /* Whether SSL_CTX_set_ciphersuites is available. * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no - * LibreSSL: no + * LibreSSL: supported since 3.4.1 (released 2021-10-14) */ -#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL)) +#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \ + !defined(OPENSSL_IS_BORINGSSL) #define HAVE_SSL_CTX_SET_CIPHERSUITES #if !defined(OPENSSL_IS_AWSLC) #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH @@ -266,7 +269,7 @@ #define HAVE_OPENSSL_VERSION #endif -#ifdef OPENSSL_IS_BORINGSSL +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) typedef uint32_t sslerr_t; #else typedef unsigned long sslerr_t; @@ -288,7 +291,7 @@ typedef unsigned long sslerr_t; #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) #endif /* !LIBRESSL_VERSION_NUMBER */ -struct ssl_backend_data { +struct ossl_ssl_backend_data { /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; @@ -714,16 +717,18 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_SEND_ERROR; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", - blen, (int)nwritten, result)); + CURL_TRC_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", + blen, (int)nwritten, result); BIO_clear_retry_flags(bio); - connssl->backend->io_result = result; + backend->io_result = result; if(nwritten < 0) { if(CURLE_AGAIN == result) BIO_set_retry_write(bio); @@ -735,6 +740,8 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_RECV_ERROR; @@ -745,10 +752,10 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", - blen, (int)nread, result)); + CURL_TRC_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", + blen, (int)nread, result); BIO_clear_retry_flags(bio); - connssl->backend->io_result = result; + backend->io_result = result; if(nread < 0) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -756,13 +763,13 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) /* Before returning server replies to the SSL instance, we need * to have setup the x509 store or verification will fail. */ - if(!connssl->backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx); + if(!backend->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); if(result) { - connssl->backend->io_result = result; + backend->io_result = result; return -1; } - connssl->backend->x509_store_setup = TRUE; + backend->x509_store_setup = TRUE; } return (int)nread; @@ -992,20 +999,6 @@ static CURLcode ossl_seed(struct Curl_easy *data) return CURLE_OK; #endif -#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET) - /* available in OpenSSL 0.9.5 and later */ - /* EGD_SOCKET is set at configure time or not at all */ - { - /* If there's an option and a define, the option overrides the - define */ - int ret = RAND_egd(EGD_SOCKET); - if(-1 != ret) { - if(rand_enough()) - return CURLE_OK; - } - } -#endif - /* fallback to a custom seeding of the PRNG using a hash based on a current time */ do { @@ -1701,7 +1694,7 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) static int ossl_init(void) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) + (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL) const uint64_t flags = #ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN /* not present in BoringSSL */ @@ -1877,7 +1870,8 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; (void)data; DEBUGASSERT(backend); @@ -1890,6 +1884,9 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); (void)SSL_shutdown(backend->handle); + + ERR_clear_error(); + SSL_set_connect_state(backend->handle); } @@ -1923,7 +1920,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf, int buffsize; int err; bool done = FALSE; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; int loop = 10; DEBUGASSERT(backend); @@ -2314,14 +2312,19 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; int i, ocsp_status; +#if defined(OPENSSL_IS_AWSLC) + const uint8_t *status; +#else unsigned char *status; +#endif const unsigned char *p; CURLcode result = CURLE_OK; OCSP_RESPONSE *rsp = NULL; OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; X509 *cert; OCSP_CERTID *id = NULL; int cert_status, crl_reason; @@ -2411,7 +2414,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, goto end; } - for(i = 0; i < sk_X509_num(ch); i++) { + for(i = 0; i < (int)sk_X509_num(ch); i++) { X509 *issuer = sk_X509_value(ch, i); if(X509_check_issued(issuer, cert) == X509_V_OK) { id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); @@ -2713,7 +2716,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ static CURLcode -set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) +ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); /* first, TLS min version... */ @@ -2810,9 +2813,9 @@ typedef long ctx_option_t; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ static CURLcode -set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_cfilter *cf, - struct Curl_easy *data) +ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, + struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; @@ -2825,8 +2828,10 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = cf->ctx; - DEBUGASSERT(connssl->backend); - SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION); + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; + DEBUGASSERT(backend); + SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else @@ -3431,7 +3436,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, const char * const ssl_cert_type = ssl_config->cert_type; const bool verifypeer = conn_config->verifypeer; char error_buffer[256]; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(backend); @@ -3573,9 +3579,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, ctx_options |= SSL_OP_NO_SSLv3; #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ - result = set_ssl_version_min_max(cf, backend->ctx); + result = ossl_set_ssl_version_min_max(cf, backend->ctx); #else - result = set_ssl_version_min_max_legacy(&ctx_options, cf, data); + result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); #endif if(result != CURLE_OK) return result; @@ -3706,6 +3712,15 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + /* When a user callback is installed to modify the SSL_CTX, + * we need to do the full initialization before calling it. + * See: #11800 */ + if(!backend->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(result) + return result; + backend->x509_store_setup = TRUE; + } Curl_set_in_callback(data, true); result = (*data->set.ssl.fsslctx)(data, backend->ctx, data->set.ssl.fsslctxp); @@ -3769,7 +3784,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -3804,7 +3819,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, { int err; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state @@ -3853,7 +3869,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; } #endif - else if(backend->io_result == CURLE_AGAIN) { +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } +#endif + if(backend->io_result == CURLE_AGAIN) { return CURLE_OK; } else { @@ -3893,11 +3915,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, error_buffer */ strcpy(error_buffer, "SSL certificate verification failed"); } -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_IS_AWSLC)) - +#if defined(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 */ else if((lib == ERR_LIB_SSL) && @@ -3967,8 +3985,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, * Heavily modified from: * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL */ -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, - const char *pinnedpubkey) +static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, + const char *pinnedpubkey) { /* Scratch */ int len1 = 0, len2 = 0; @@ -4046,7 +4064,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, char buffer[2048]; const char *ptr; BIO *mem = BIO_new(BIO_s_mem()); - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); @@ -4061,7 +4080,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, if(data->set.ssl.certinfo) /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, connssl->backend->handle); + (void)Curl_ossl_certchain(data, backend->handle); backend->server_cert = SSL_get1_peer_certificate(backend->handle); if(!backend->server_cert) { @@ -4229,7 +4248,7 @@ static CURLcode servercert(struct Curl_cfilter *cf, data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; if(!result && ptr) { - result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); + result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key"); } @@ -4398,11 +4417,13 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, static bool ossl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - struct ssl_connect_data *ctx = cf->ctx; + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; (void)data; - DEBUGASSERT(ctx && ctx->backend); - if(ctx->backend->handle && SSL_pending(ctx->backend->handle)) + DEBUGASSERT(connssl && backend); + if(backend->handle && SSL_pending(backend->handle)) return TRUE; return FALSE; } @@ -4421,7 +4442,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, int memlen; int rc; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; (void)data; DEBUGASSERT(backend); @@ -4517,7 +4539,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, int buffsize; struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; (void)data; DEBUGASSERT(backend); @@ -4718,7 +4741,10 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ mdctx = EVP_MD_CTX_create(); if(!mdctx) return CURLE_OUT_OF_MEMORY; - EVP_DigestInit(mdctx, EVP_sha256()); + if(!EVP_DigestInit(mdctx, EVP_sha256())) { + EVP_MD_CTX_destroy(mdctx); + return CURLE_FAILED_INIT; + } EVP_DigestUpdate(mdctx, tmp, tmplen); EVP_DigestFinal_ex(mdctx, sha256sum, &len); EVP_MD_CTX_destroy(mdctx); @@ -4740,7 +4766,8 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ - struct ssl_backend_data *backend = connssl->backend; + struct ossl_ssl_backend_data *backend = + (struct ossl_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); return info == CURLINFO_TLS_SESSION ? (void *)backend->ctx : (void *)backend->handle; @@ -4773,7 +4800,7 @@ const struct Curl_ssl Curl_ssl_openssl = { #endif SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct ossl_ssl_backend_data), ossl_init, /* init */ ossl_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/rustls.c b/vendor/curl/lib/vtls/rustls.c index 097c58ce1c..a3e9d964c9 100644 --- a/vendor/curl/lib/vtls/rustls.c +++ b/vendor/curl/lib/vtls/rustls.c @@ -40,7 +40,7 @@ #include "strerror.h" #include "multiif.h" -struct ssl_backend_data +struct rustls_ssl_backend_data { const struct rustls_client_config *config; struct rustls_connection *conn; @@ -67,10 +67,12 @@ static bool cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct rustls_ssl_backend_data *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - return ctx->backend->data_pending; + backend = (struct rustls_ssl_backend_data *)ctx->backend; + return backend->data_pending; } static CURLcode @@ -102,10 +104,6 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) ret = EINVAL; } *out_n = (int)nread; - /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", - len, nread, result)); - */ return ret; } @@ -126,7 +124,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) } *out_n = (int)nwritten; /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", + CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", len, nwritten, result)); */ return ret; @@ -136,7 +134,8 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode *err) { struct ssl_connect_data *const connssl = cf->ctx; - struct ssl_backend_data *const backend = connssl->backend; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; struct io_ctx io_ctx; size_t tls_bytes_read = 0; rustls_io_result io_error; @@ -191,7 +190,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *plainbuf, size_t plainlen, CURLcode *err) { struct ssl_connect_data *const connssl = cf->ctx; - struct ssl_backend_data *const backend = connssl->backend; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; size_t n = 0; size_t plain_bytes_copied = 0; @@ -263,8 +263,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", - plainlen, nread, *err)); + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", + plainlen, nread, *err); return nread; } @@ -283,7 +283,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *plainbuf, size_t plainlen, CURLcode *err) { struct ssl_connect_data *const connssl = cf->ctx; - struct ssl_backend_data *const backend = connssl->backend; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; struct io_ctx io_ctx; size_t plainwritten = 0; @@ -297,7 +298,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); rconn = backend->conn; - DEBUGF(LOG_CF(data, cf, "cf_send: %ld plain bytes", plainlen)); + CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes", plainlen); io_ctx.cf = cf; io_ctx.data = data; @@ -322,8 +323,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, &tlswritten); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "cf_send: EAGAIN after %zu bytes", - tlswritten_total)); + CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", + tlswritten_total); *err = CURLE_AGAIN; return -1; } @@ -339,7 +340,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_WRITE_ERROR; return -1; } - DEBUGF(LOG_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten)); + CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten); tlswritten_total += tlswritten; } @@ -373,7 +374,7 @@ cr_hostname_is_ip(const char *hostname) static CURLcode cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, - struct ssl_backend_data *const backend) + struct rustls_ssl_backend_data *const backend) { struct ssl_connect_data *connssl = cf->ctx; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -491,7 +492,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, { struct ssl_connect_data *const connssl = cf->ctx; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct ssl_backend_data *const backend = connssl->backend; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; int result; @@ -504,7 +506,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(ssl_connection_none == connssl->state) { - result = cr_init_backend(cf, data, connssl->backend); + result = cr_init_backend(cf, data, + (struct rustls_ssl_backend_data *)connssl->backend); if(result != CURLE_OK) { return result; } @@ -594,7 +597,8 @@ cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, { struct ssl_connect_data *const connssl = cf->ctx; curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct ssl_backend_data *const backend = connssl->backend; + struct rustls_ssl_backend_data *const backend = + (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; (void)data; @@ -617,7 +621,8 @@ static void * cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); return &backend->conn; } @@ -626,7 +631,8 @@ static void cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; CURLcode tmperr = CURLE_OK; ssize_t n = 0; @@ -659,7 +665,7 @@ const struct Curl_ssl Curl_ssl_rustls = { SSLSUPP_CAINFO_BLOB | /* supports */ SSLSUPP_TLS13_CIPHERSUITES | SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct rustls_ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/schannel.c b/vendor/curl/lib/vtls/schannel.c index 513811d2d8..f6a5d441a9 100644 --- a/vendor/curl/lib/vtls/schannel.c +++ b/vendor/curl/lib/vtls/schannel.c @@ -33,13 +33,12 @@ #ifdef USE_SCHANNEL -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS - #ifndef USE_WINDOWS_SSPI # error "Can't compile SCHANNEL support without SSPI." #endif #include "schannel.h" +#include "schannel_int.h" #include "vtls.h" #include "vtls_int.h" #include "strcase.h" @@ -186,9 +185,9 @@ #define PKCS12_NO_PERSIST_KEY 0x00008000 #endif -static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey); +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey); static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, void *BufDataPtr, unsigned long BufByteSize) @@ -207,9 +206,9 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, } static CURLcode -set_ssl_version_min_max(DWORD *enabled_protocols, - struct Curl_cfilter *cf, - struct Curl_easy *data) +schannel_set_ssl_version_min_max(DWORD *enabled_protocols, + struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; @@ -500,7 +499,8 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, DWORD flags = 0; DWORD enabled_protocols = 0; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)(connssl->backend); DEBUGASSERT(backend); @@ -563,7 +563,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { - result = set_ssl_version_min_max(&enabled_protocols, cf, data); + result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data); if(result != CURLE_OK) return result; break; @@ -793,8 +793,11 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, backend->cred->client_cert_store = client_cert_store; #endif - /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */ - if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as + long as the user did not set a legacy algorithm list + (CURLOPT_SSL_CIPHER_LIST). */ + if(!conn_config->cipher_list && + curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { char *ciphers13 = 0; @@ -807,9 +810,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, SCH_CREDENTIALS credentials = { 0 }; TLS_PARAMETERS tls_parameters = { 0 }; - CRYPTO_SETTINGS crypto_settings[4] = { 0 }; - UNICODE_STRING blocked_ccm_modes[1] = { 0 }; - UNICODE_STRING blocked_gcm_modes[1] = { 0 }; + CRYPTO_SETTINGS crypto_settings[4] = { { 0 } }; + UNICODE_STRING blocked_ccm_modes[1] = { { 0 } }; + UNICODE_STRING blocked_gcm_modes[1] = { { 0 } }; int crypto_settings_idx = 0; @@ -844,7 +847,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, /* reject too-long cipher names */ if(n > (LONGEST_ALG_ID - 1)) { - failf(data, "Cipher name too long, not checked."); + failf(data, "schannel: Cipher name too long, not checked"); return CURLE_SSL_CIPHER; } @@ -872,7 +875,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, disable_aes_ccm_sha256 = FALSE; } else { - failf(data, "Passed in an unknown TLS 1.3 cipher."); + failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp); return CURLE_SSL_CIPHER; } @@ -887,7 +890,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256 && disable_chacha_poly && disable_aes_ccm_8_sha256 && disable_aes_ccm_sha256) { - failf(data, "All available TLS 1.3 ciphers were disabled."); + failf(data, "schannel: All available TLS 1.3 ciphers were disabled"); return CURLE_SSL_CIPHER; } @@ -1010,7 +1013,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, &backend->cred->time_stamp); } else { - /* Pre-Windows 10 1809 */ + /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS + doesn't document it, currently Schannel will not negotiate TLS 1.3 when + SCHANNEL_CRED is used. */ ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; SCHANNEL_CRED schannel_cred = { 0 }; @@ -1019,9 +1024,20 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, schannel_cred.grbitEnabledProtocols = enabled_protocols; if(ciphers) { + if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + infof(data, "schannel: WARNING: This version of Schannel may " + "negotiate a less-secure TLS version than TLS 1.3 because the " + "user set an algorithm cipher list."); + } + if(conn_config->cipher_list13) { + failf(data, "schannel: This version of Schannel does not support " + "setting an algorithm cipher list and TLS 1.3 cipher list at " + "the same time"); + return CURLE_SSL_CIPHER; + } result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to from connection ssl config"); + failf(data, "schannel: Failed setting algorithm cipher list"); return result; } } @@ -1075,7 +1091,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { ssize_t written = -1; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); SecBuffer outbuf; @@ -1157,7 +1174,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) { backend->cred = old_cred; - DEBUGF(infof(data, "schannel: re-using existing credential handle")); + DEBUGF(infof(data, "schannel: reusing existing credential handle")); /* increment the reference counter of the credential/session handle */ backend->cred->refcount++; @@ -1349,7 +1366,8 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); int i; ssize_t nread = -1, written = -1; @@ -1607,7 +1625,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; if(pubkey_ptr) { - result = pkp_pin_peer_pubkey(cf, data, pubkey_ptr); + result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr); if(result) { failf(data, "SSL: public key does not match pinned public key"); return result; @@ -1616,10 +1634,16 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef HAS_MANUAL_VERIFY_API if(conn_config->verifypeer && backend->use_manual_cred_validation) { + /* Certificate verification also verifies the hostname if verifyhost */ return Curl_verify_certificate(cf, data); } #endif + /* Verify the hostname manually when certificate verification is disabled, + because in that case Schannel won't verify it. */ + if(!conn_config->verifypeer && conn_config->verifyhost) + return Curl_verify_host(cf, data); + return CURLE_OK; } @@ -1632,7 +1656,8 @@ valid_cert_encoding(const CERT_CONTEXT *cert_context) (cert_context->cbCertEncoded > 0); } -typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg); +typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *arg); static void traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, @@ -1640,19 +1665,32 @@ traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, { const CERT_CONTEXT *current_context = NULL; bool should_continue = true; + bool first = true; + bool reverse_order = false; while(should_continue && (current_context = CertEnumCertificatesInStore( context->hCertStore, - current_context)) != NULL) - should_continue = func(current_context, arg); + current_context)) != NULL) { + /* Windows 11 22H2 OS Build 22621.674 or higher enumerates certificates in + leaf-to-root order while all previous versions of Windows enumerate + certificates in root-to-leaf order. Determine the order of enumeration + by comparing SECPKG_ATTR_REMOTE_CERT_CONTEXT's pbCertContext with the + first certificate's pbCertContext. */ + if(first && context->pbCertEncoded != current_context->pbCertEncoded) + reverse_order = true; + should_continue = func(current_context, reverse_order, arg); + first = false; + } if(current_context) CertFreeCertificateContext(current_context); } static bool -cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count) +cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *certs_count) { + (void)reverse_order; /* unused */ if(valid_cert_encoding(ccert_context)) (*(int *)certs_count)++; return true; @@ -1667,14 +1705,16 @@ struct Adder_args }; static bool -add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) +add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *raw_arg) { struct Adder_args *args = (struct Adder_args*)raw_arg; args->result = CURLE_OK; if(valid_cert_encoding(ccert_context)) { const char *beg = (const char *) ccert_context->pbCertEncoded; const char *end = beg + ccert_context->cbCertEncoded; - int insert_index = (args->certs_count - 1) - args->idx; + int insert_index = reverse_order ? (args->certs_count - 1) - args->idx : + args->idx; args->result = Curl_extract_certinfo(args->data, insert_index, beg, end); args->idx++; @@ -1686,7 +1726,8 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = CURLE_OK; SECURITY_STATUS sspi_status = SEC_E_OK; @@ -1755,7 +1796,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif - /* save the current session data for possible re-use */ + /* save the current session data for possible reuse */ if(ssl_config->primary.sessionid) { bool incache; bool added = FALSE; @@ -1931,7 +1972,8 @@ schannel_connect_common(struct Curl_cfilter *cf, * Available on Windows 7 or later. */ { - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); cf->conn->sslContext = &backend->ctxt->ctxt_handle; } @@ -1960,7 +2002,8 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, SecBufferDesc outbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); @@ -2110,7 +2153,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* we want the length of the encrypted buffer to be at least large enough that it can hold all the bytes requested and some TLS record overhead. */ size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); @@ -2443,12 +2487,13 @@ static bool schannel_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { const struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; (void)data; DEBUGASSERT(backend); - if(connssl->backend->ctxt) /* SSL/TLS is in use */ + if(backend->ctxt) /* SSL/TLS is in use */ return (backend->decdata_offset > 0 || (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); else @@ -2486,12 +2531,13 @@ static int schannel_shutdown(struct Curl_cfilter *cf, * Shutting Down an Schannel Connection */ struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(data); DEBUGASSERT(backend); - if(connssl->backend->ctxt) { + if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", connssl->hostname, connssl->port); } @@ -2611,12 +2657,13 @@ static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM, return Curl_win32_random(entropy, length); } -static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *pinnedpubkey) +static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *pinnedpubkey) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; CERT_CONTEXT *pCertContextServer = NULL; /* Result is returned to caller */ @@ -2742,7 +2789,8 @@ static CURLcode schannel_sha256sum(const unsigned char *input, static void *schannel_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; (void)info; DEBUGASSERT(backend); return &backend->ctxt->ctxt_handle; @@ -2759,7 +2807,7 @@ const struct Curl_ssl Curl_ssl_schannel = { SSLSUPP_TLS13_CIPHERSUITES | SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct schannel_ssl_backend_data), schannel_init, /* init */ schannel_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/schannel.h b/vendor/curl/lib/vtls/schannel.h index 7fae39fa0a..be23567302 100644 --- a/vendor/curl/lib/vtls/schannel.h +++ b/vendor/curl/lib/vtls/schannel.h @@ -28,8 +28,6 @@ #ifdef USE_SCHANNEL -#define SCHANNEL_USE_BLACKLISTS 1 - #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4201) @@ -78,122 +76,11 @@ extern const struct Curl_ssl Curl_ssl_schannel; +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data); + CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, struct Curl_easy *data); -/* structs to expose only in schannel.c and schannel_verify.c */ -#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS - -#ifdef __MINGW32__ -#ifdef __MINGW64_VERSION_MAJOR -#define HAS_MANUAL_VERIFY_API -#endif -#else -#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN -#define HAS_MANUAL_VERIFY_API -#endif -#endif - -#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ - && !defined(DISABLE_SCHANNEL_CLIENT_CERT) -#define HAS_CLIENT_CERT_PATH -#endif - -#ifndef SCH_CREDENTIALS_VERSION - -#define SCH_CREDENTIALS_VERSION 0x00000005 - -typedef enum _eTlsAlgorithmUsage -{ - TlsParametersCngAlgUsageKeyExchange, - TlsParametersCngAlgUsageSignature, - TlsParametersCngAlgUsageCipher, - TlsParametersCngAlgUsageDigest, - TlsParametersCngAlgUsageCertSig -} eTlsAlgorithmUsage; - -typedef struct _CRYPTO_SETTINGS -{ - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; - DWORD dwMinBitLength; - DWORD dwMaxBitLength; -} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; - -typedef struct _TLS_PARAMETERS -{ - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; -} TLS_PARAMETERS, * PTLS_PARAMETERS; - -typedef struct _SCH_CREDENTIALS -{ - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; - - DWORD cMappers; - struct _HMAPPER **aphMappers; - - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; -} SCH_CREDENTIALS, * PSCH_CREDENTIALS; - -#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 -#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 -#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 -#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 - -#endif - -struct Curl_schannel_cred { - CredHandle cred_handle; - TimeStamp time_stamp; - TCHAR *sni_hostname; -#ifdef HAS_CLIENT_CERT_PATH - HCERTSTORE client_cert_store; -#endif - int refcount; -}; - -struct Curl_schannel_ctxt { - CtxtHandle ctxt_handle; - TimeStamp time_stamp; -}; - -struct ssl_backend_data { - struct Curl_schannel_cred *cred; - struct Curl_schannel_ctxt *ctxt; - SecPkgContext_StreamSizes stream_sizes; - size_t encdata_length, decdata_length; - size_t encdata_offset, decdata_offset; - unsigned char *encdata_buffer, *decdata_buffer; - /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another recv() (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds - more bytes into encdata then set this back to false. */ - bool encdata_is_incomplete; - unsigned long req_flags, ret_flags; - CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ - bool recv_sspi_close_notify; /* true if connection closed by close_notify */ - bool recv_connection_closed; /* true if connection closed, regardless how */ - bool recv_renegotiating; /* true if recv is doing renegotiation */ - bool use_alpn; /* true if ALPN is used for this connection */ -#ifdef HAS_MANUAL_VERIFY_API - bool use_manual_cred_validation; /* true if manual cred validation is used */ -#endif -}; -#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ - #endif /* USE_SCHANNEL */ #endif /* HEADER_CURL_SCHANNEL_H */ diff --git a/vendor/curl/lib/vtls/schannel_int.h b/vendor/curl/lib/vtls/schannel_int.h new file mode 100644 index 0000000000..edb20bcd23 --- /dev/null +++ b/vendor/curl/lib/vtls/schannel_int.h @@ -0,0 +1,194 @@ +#ifndef HEADER_CURL_SCHANNEL_INT_H +#define HEADER_CURL_SCHANNEL_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Marc Hoersken, , et al. + * 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" + +#ifdef USE_SCHANNEL + +#ifdef __MINGW32__ +#ifdef __MINGW64_VERSION_MAJOR +#define HAS_MANUAL_VERIFY_API +#endif +#else +#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN +#define HAS_MANUAL_VERIFY_API +#endif +#endif + +#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ + && !defined(DISABLE_SCHANNEL_CLIENT_CERT) +#define HAS_CLIENT_CERT_PATH +#endif + +#ifndef CRYPT_DECODE_NOCOPY_FLAG +#define CRYPT_DECODE_NOCOPY_FLAG 0x1 +#endif + +#ifndef CRYPT_DECODE_ALLOC_FLAG +#define CRYPT_DECODE_ALLOC_FLAG 0x8000 +#endif + +#ifndef CERT_ALT_NAME_DNS_NAME +#define CERT_ALT_NAME_DNS_NAME 3 +#endif + +#ifndef CERT_ALT_NAME_IP_ADDRESS +#define CERT_ALT_NAME_IP_ADDRESS 8 +#endif + + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +/* Original mingw is missing CERT structs or they're disabled. + Refer to w32api-5.0.2-mingw32-dev\include\wincrypt.h. */ + +/* !checksrc! disable TYPEDEFSTRUCT 4 */ +typedef struct _CERT_OTHER_NAME { + LPSTR pszObjId; + CRYPT_OBJID_BLOB Value; +} CERT_OTHER_NAME, *PCERT_OTHER_NAME; + +typedef struct _CERT_ALT_NAME_ENTRY { + DWORD dwAltNameChoice; + union { + PCERT_OTHER_NAME pOtherName; + LPWSTR pwszRfc822Name; + LPWSTR pwszDNSName; + CERT_NAME_BLOB DirectoryName; + LPWSTR pwszURL; + CRYPT_DATA_BLOB IPAddress; + LPSTR pszRegisteredID; + }; +} CERT_ALT_NAME_ENTRY, *PCERT_ALT_NAME_ENTRY; + +typedef struct _CERT_ALT_NAME_INFO { + DWORD cAltEntry; + PCERT_ALT_NAME_ENTRY rgAltEntry; +} CERT_ALT_NAME_INFO, *PCERT_ALT_NAME_INFO; + +typedef struct _CRYPT_DECODE_PARA { + DWORD cbSize; + PFN_CRYPT_ALLOC pfnAlloc; + PFN_CRYPT_FREE pfnFree; +} CRYPT_DECODE_PARA, *PCRYPT_DECODE_PARA; +#endif + +#ifndef SCH_CREDENTIALS_VERSION + +#define SCH_CREDENTIALS_VERSION 0x00000005 + +typedef enum _eTlsAlgorithmUsage +{ + TlsParametersCngAlgUsageKeyExchange, + TlsParametersCngAlgUsageSignature, + TlsParametersCngAlgUsageCipher, + TlsParametersCngAlgUsageDigest, + TlsParametersCngAlgUsageCertSig +} eTlsAlgorithmUsage; + +typedef struct _CRYPTO_SETTINGS +{ + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + +typedef struct _TLS_PARAMETERS +{ + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, * PTLS_PARAMETERS; + +typedef struct _SCH_CREDENTIALS +{ + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + + DWORD cMappers; + struct _HMAPPER **aphMappers; + + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + +#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 +#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 +#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 +#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 + +#endif /* SCH_CREDENTIALS_VERSION */ + +struct Curl_schannel_cred { + CredHandle cred_handle; + TimeStamp time_stamp; + TCHAR *sni_hostname; +#ifdef HAS_CLIENT_CERT_PATH + HCERTSTORE client_cert_store; +#endif + int refcount; +}; + +struct Curl_schannel_ctxt { + CtxtHandle ctxt_handle; + TimeStamp time_stamp; +}; + +struct schannel_ssl_backend_data { + struct Curl_schannel_cred *cred; + struct Curl_schannel_ctxt *ctxt; + SecPkgContext_StreamSizes stream_sizes; + size_t encdata_length, decdata_length; + size_t encdata_offset, decdata_offset; + unsigned char *encdata_buffer, *decdata_buffer; + /* encdata_is_incomplete: if encdata contains only a partial record that + can't be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds + more bytes into encdata then set this back to false. */ + bool encdata_is_incomplete; + unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ + bool recv_renegotiating; /* true if recv is doing renegotiation */ + bool use_alpn; /* true if ALPN is used for this connection */ +#ifdef HAS_MANUAL_VERIFY_API + bool use_manual_cred_validation; /* true if manual cred validation is used */ +#endif +}; + +#endif /* USE_SCHANNEL */ +#endif /* HEADER_CURL_SCHANNEL_INT_H */ diff --git a/vendor/curl/lib/vtls/schannel_verify.c b/vendor/curl/lib/vtls/schannel_verify.c index d75ee8dfe7..a5d5c98bb7 100644 --- a/vendor/curl/lib/vtls/schannel_verify.c +++ b/vendor/curl/lib/vtls/schannel_verify.c @@ -36,10 +36,8 @@ # error "Can't compile SCHANNEL support without SSPI." #endif -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS #include "schannel.h" - -#ifdef HAS_MANUAL_VERIFY_API +#include "schannel_int.h" #include "vtls.h" #include "vtls_int.h" @@ -54,7 +52,10 @@ #include "curl_memory.h" #include "memdebug.h" -#define BACKEND connssl->backend +#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) + + +#ifdef HAS_MANUAL_VERIFY_API #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" @@ -330,6 +331,8 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, return result; } +#endif /* HAS_MANUAL_VERIFY_API */ + /* * Returns the number of characters necessary to populate all the host_names. * If host_names is not NULL, populate it with all the host names. Each string @@ -353,10 +356,10 @@ static DWORD cert_get_name_string(struct Curl_easy *data, LPTSTR current_pos = NULL; DWORD i; +#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CertGetNameString will provide the 8-bit character string without * any decoding */ DWORD name_flags = @@ -368,8 +371,8 @@ static DWORD cert_get_name_string(struct Curl_easy *data, host_names, length); return actual_length; -#endif } +#endif compute_content = host_names != NULL && length != 0; @@ -457,17 +460,34 @@ static DWORD cert_get_name_string(struct Curl_easy *data, return actual_length; } -static CURLcode verify_host(struct Curl_easy *data, - CERT_CONTEXT *pCertContextServer, - const char *conn_hostname) +/* Verify the server's hostname */ +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data) { + struct ssl_connect_data *connssl = cf->ctx; + SECURITY_STATUS sspi_status; CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + CERT_CONTEXT *pCertContextServer = NULL; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; + const char *conn_hostname = connssl->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + /* Determine the size of the string needed for the cert hostname */ len = cert_get_name_string(data, pCertContextServer, NULL, 0); if(len == 0) { @@ -498,10 +518,9 @@ static CURLcode verify_host(struct Curl_easy *data, goto cleanup; } - /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output - * will contain all DNS names, where each name is null-terminated - * and the last DNS name is double null-terminated. Due to this - * encoding, use the length of the buffer to iterate over all names. + /* cert_hostname_buff contains all DNS names, where each name is + * null-terminated and the last DNS name is double null-terminated. Due to + * this encoding, use the length of the buffer to iterate over all names. */ result = CURLE_PEER_FAILED_VERIFICATION; while(cert_hostname_buff_index < len && @@ -560,9 +579,15 @@ static CURLcode verify_host(struct Curl_easy *data, cleanup: Curl_safefree(cert_hostname_buff); + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + return result; } + +#ifdef HAS_MANUAL_VERIFY_API +/* Verify the server's certificate and hostname */ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -721,7 +746,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(result == CURLE_OK) { if(conn_config->verifyhost) { - result = verify_host(data, pCertContextServer, connssl->hostname); + result = Curl_verify_host(cf, data); } } diff --git a/vendor/curl/lib/vtls/sectransp.c b/vendor/curl/lib/vtls/sectransp.c index c9f02f2d5b..e6a114ad5f 100644 --- a/vendor/curl/lib/vtls/sectransp.c +++ b/vendor/curl/lib/vtls/sectransp.c @@ -146,7 +146,7 @@ #define ioErr -36 #define paramErr -50 -struct ssl_backend_data { +struct st_ssl_backend_data { SSLContextRef ssl_ctx; bool ssl_direction; /* true if writing, false if reading */ size_t ssl_write_buffered_length; @@ -836,7 +836,8 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, { struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result; @@ -844,8 +845,8 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, DEBUGASSERT(data); nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", - *dataLength, nread, result)); + CURL_TRC_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", + *dataLength, nread, result); if(nread < 0) { switch(result) { case CURLE_OK: @@ -859,6 +860,9 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, } nread = 0; } + else if(nread == 0) { + rtn = errSSLClosedGraceful; + } else if((size_t)nread < *dataLength) { rtn = errSSLWouldBlock; } @@ -872,7 +876,8 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection, { struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result; @@ -880,8 +885,8 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection, DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", - *dataLength, nwritten, result)); + CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", + *dataLength, nwritten, result); if(nwritten <= 0) { if(result == CURLE_AGAIN) { rtn = errSSLWouldBlock; @@ -1081,7 +1086,6 @@ static OSStatus CopyIdentityWithLabel(char *label, CFArrayRef keys_list; CFIndex keys_list_count; CFIndex i; - CFStringRef common_name; /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. kSecClassIdentity was introduced in Lion. If both exist, let's use them @@ -1129,6 +1133,7 @@ static OSStatus CopyIdentityWithLabel(char *label, (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i); err = SecIdentityCopyCertificate(identity, &cert); if(err == noErr) { + CFStringRef common_name = NULL; OSStatus copy_status = noErr; #if CURL_BUILD_IOS common_name = SecCertificateCopySubjectSummary(cert); @@ -1144,7 +1149,8 @@ static OSStatus CopyIdentityWithLabel(char *label, status = noErr; break; } - CFRelease(common_name); + if(common_name) + CFRelease(common_name); } CFRelease(cert); } @@ -1288,7 +1294,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, /* This code was borrowed from nss.c, with some modifications: * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. + * be loaded as a PEM or a nickname. * * returns 1 for a file * returns 0 for not a file @@ -1338,7 +1344,8 @@ static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; @@ -1605,7 +1612,7 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, The message is a bit cryptic and longer than necessary but can be understood by humans. */ failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name" - " starting position %d and ending position %d", + " starting position %zd and ending position %zd", ciphers, cipher_start - ciphers, cipher_end - ciphers); @@ -1633,7 +1640,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); const struct curl_blob *ssl_cablob = conn_config->ca_info_blob; @@ -1655,7 +1663,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step1")); + CURL_TRC_CF(data, cf, "connect_step1"); GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -2062,7 +2070,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } /* If there isn't one, then let's make one up! This has to be done prior to starting the handshake. */ @@ -2286,7 +2294,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); + CURL_TRC_CF(data, cf, "append_cert for CA failed"); result = rc; goto out; } @@ -2300,7 +2308,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); + CURL_TRC_CF(data, cf, "append_cert for CA failed"); result = rc; goto out; } @@ -2316,7 +2324,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, goto out; } - DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n)); + CURL_TRC_CF(data, cf, "setting %d trust anchors", n); ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); @@ -2338,11 +2346,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, switch(trust_eval) { case kSecTrustResultUnspecified: /* what does this really mean? */ - DEBUGF(LOG_CF(data, cf, "trust result: Unspecified")); + CURL_TRC_CF(data, cf, "trust result: Unspecified"); result = CURLE_OK; goto out; case kSecTrustResultProceed: - DEBUGF(LOG_CF(data, cf, "trust result: Proceed")); + CURL_TRC_CF(data, cf, "trust result: Proceed"); result = CURLE_OK; goto out; @@ -2375,7 +2383,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, size_t buflen; if(ca_info_blob) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob")); + CURL_TRC_CF(data, cf, "verify_peer, CA from config blob"); certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); if(!certbuf) { return CURLE_OUT_OF_MEMORY; @@ -2385,7 +2393,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, certbuf[ca_info_blob->len]='\0'; } else if(cafile) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile)); + CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile); if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); return CURLE_SSL_CACERT_BADFILE; @@ -2425,7 +2433,6 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, SecTrustRef trust; OSStatus ret; SecKeyRef keyRef; - OSStatus success; ret = SSLCopyPeerTrust(ctx, &trust); if(ret != noErr || !trust) @@ -2445,11 +2452,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, #elif SECTRANSP_PINNEDPUBKEY_V2 - success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); - CFRelease(keyRef); - if(success != errSecSuccess || !publicKeyBits) - break; + { + OSStatus success; + success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, + &publicKeyBits); + CFRelease(keyRef); + if(success != errSecSuccess || !publicKeyBits) + break; + } #endif /* SECTRANSP_PINNEDPUBKEY_V2 */ @@ -2477,7 +2487,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, spkiHeaderLength = 23; break; default: - infof(data, "SSL: unhandled public key length: %d", pubkeylen); + infof(data, "SSL: unhandled public key length: %zu", pubkeylen); #elif SECTRANSP_PINNEDPUBKEY_V2 default: /* ecDSA secp256r1 pubkeylen == 91 header already included? @@ -2515,7 +2525,8 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); OSStatus err; SSLCipherSuite cipher; @@ -2525,7 +2536,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step2")); + CURL_TRC_CF(data, cf, "connect_step2"); /* Here goes nothing: */ check_handshake: @@ -2896,7 +2907,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, CURLcode result = ssl_config->certinfo ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; CFArrayRef server_certs = NULL; SecCertificateRef server_cert; OSStatus err; @@ -2991,7 +3003,7 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; CURLcode result; - DEBUGF(LOG_CF(data, cf, "connect_step3")); + CURL_TRC_CF(data, cf, "connect_step3"); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ @@ -3100,7 +3112,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_done == connssl->connecting_state) { - DEBUGF(LOG_CF(data, cf, "connected")); + CURL_TRC_CF(data, cf, "connected"); connssl->state = ssl_connection_complete; *done = TRUE; } @@ -3139,14 +3151,15 @@ static CURLcode sectransp_connect(struct Curl_cfilter *cf, static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; (void) data; DEBUGASSERT(backend); if(backend->ssl_ctx) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext) @@ -3166,7 +3179,8 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; ssize_t nread; int what; int rc; @@ -3191,7 +3205,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); - DEBUGF(LOG_CF(data, cf, "shutdown")); + CURL_TRC_CF(data, cf, "shutdown"); while(loop--) { if(what < 0) { /* anything that gets here is fatally bad */ @@ -3244,7 +3258,8 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { const struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; OSStatus err; size_t buffer; @@ -3252,7 +3267,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { /* SSL is in use */ - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; @@ -3287,6 +3302,7 @@ static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ unsigned char *sha256sum, /* output */ size_t sha256len) { + (void)sha256len; assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); return CURLE_OK; @@ -3308,7 +3324,8 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; size_t processed = 0UL; OSStatus err; @@ -3376,7 +3393,8 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); size_t processed = 0UL; OSStatus err; @@ -3434,7 +3452,8 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf, static void *sectransp_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; (void)info; DEBUGASSERT(backend); return backend->ssl_ctx; @@ -3450,7 +3469,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { #endif /* SECTRANSP_PINNEDPUBKEY */ SSLSUPP_HTTPS_PROXY, - sizeof(struct ssl_backend_data), + sizeof(struct st_ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/vtls.c b/vendor/curl/lib/vtls/vtls.c index a4ff7d61a6..38a20e8bea 100644 --- a/vendor/curl/lib/vtls/vtls.c +++ b/vendor/curl/lib/vtls/vtls.c @@ -417,7 +417,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGASSERT(ssl_config->primary.sessionid); if(!ssl_config->primary.sessionid || !data->state.session) - /* session ID re-use is disabled or the session cache has not been + /* session ID reuse is disabled or the session cache has not been setup */ return TRUE; @@ -453,7 +453,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, } } - DEBUGF(infof(data, DMSG(data, "%s Session ID in cache for %s %s://%s:%d"), + DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", cf->conn->handler->scheme, connssl->hostname, connssl->port)); @@ -601,8 +601,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, if(added) *added = TRUE; - DEBUGF(infof(data, DMSG(data, "Added Session ID to cache for %s://%s:%d" - " [%s]"), store->scheme, store->name, store->remote_port, + DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]", + store->scheme, store->name, store->remote_port, Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server")); return CURLE_OK; } @@ -635,19 +635,16 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_connect_data *connssl = cf->ctx; curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - if(sock != CURL_SOCKET_BAD) { - if(connssl->connecting_state == ssl_connect_2_writing) { - /* write mode */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = sock; - return GETSOCK_READSOCK(0); - } + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + + if(connssl->connecting_state == ssl_connect_2_writing) { + /* we are only interested in writing */ + socks[0] = sock; + return GETSOCK_WRITESOCK(0); } - return GETSOCK_BLANK; + socks[0] = sock; + return GETSOCK_READSOCK(0); } /* Selects an SSL crypto engine @@ -883,6 +880,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, FILE *fp; unsigned char *buf = NULL, *pem_ptr = NULL; CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) @@ -893,8 +893,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, /* only do this if pinnedpubkey starts with "sha256//", length 8 */ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { CURLcode encode; - size_t encodedlen, pinkeylen; - char *encoded, *pinkeycopy, *begin_pos, *end_pos; + size_t encodedlen = 0, pinkeylen; + char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos; unsigned char *sha256sumdigest; if(!Curl_ssl->sha256sum) { @@ -907,14 +907,12 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, if(!sha256sumdigest) return CURLE_OUT_OF_MEMORY; encode = Curl_ssl->sha256sum(pubkey, pubkeylen, - sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); - - if(encode != CURLE_OK) - return encode; + sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); - encode = Curl_base64_encode((char *)sha256sumdigest, - CURL_SHA256_DIGEST_LENGTH, &encoded, - &encodedlen); + if(!encode) + encode = Curl_base64_encode((char *)sha256sumdigest, + CURL_SHA256_DIGEST_LENGTH, &encoded, + &encodedlen); Curl_safefree(sha256sumdigest); if(encode) @@ -1242,12 +1240,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_sectransp; #elif defined(USE_GNUTLS) &Curl_ssl_gnutls; -#elif defined(USE_GSKIT) - &Curl_ssl_gskit; #elif defined(USE_MBEDTLS) &Curl_ssl_mbedtls; -#elif defined(USE_NSS) - &Curl_ssl_nss; #elif defined(USE_RUSTLS) &Curl_ssl_rustls; #elif defined(USE_OPENSSL) @@ -1270,15 +1264,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_GNUTLS) &Curl_ssl_gnutls, #endif -#if defined(USE_GSKIT) - &Curl_ssl_gskit, -#endif #if defined(USE_MBEDTLS) &Curl_ssl_mbedtls, #endif -#if defined(USE_NSS) - &Curl_ssl_nss, -#endif #if defined(USE_OPENSSL) &Curl_ssl_openssl, #endif @@ -1506,7 +1494,7 @@ static void ssl_cf_close(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); cf_close(cf, data); - cf->next->cft->close(cf->next, data); + cf->next->cft->do_close(cf->next, data); CF_DATA_RESTORE(cf, save); } @@ -1524,13 +1512,14 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, } CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "cf_connect()"); (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); DEBUGASSERT(connssl); DEBUGASSERT(cf->conn->host.name); - result = cf->next->cft->connect(cf->next, data, blocking, done); + result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) goto out; @@ -1553,6 +1542,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, DEBUGASSERT(connssl->state == ssl_connection_complete); } out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1594,6 +1584,7 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, ssize_t nread; CF_DATA_SAVE(save, cf, data); + *err = CURLE_OK; nread = Curl_ssl->recv_plain(cf, data, buf, len, err); if(nread > 0) { DEBUGASSERT((size_t)nread <= len); @@ -1602,7 +1593,7 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, /* eof */ *err = CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err)); + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1612,12 +1603,17 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, curl_socket_t *socks) { struct cf_call_data save; - int result; + int fds = GETSOCK_BLANK; - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_select_socks(cf, data, socks); - CF_DATA_RESTORE(cf, save); - return result; + if(!cf->next->connected) { + fds = cf->next->cft->get_select_socks(cf->next, data, socks); + } + else if(!cf->connected) { + CF_DATA_SAVE(save, cf, data); + fds = Curl_ssl->get_select_socks(cf, data, socks); + CF_DATA_RESTORE(cf, save); + } + return fds; } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, @@ -1703,7 +1699,7 @@ static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, struct Curl_cftype Curl_cft_ssl = { "SSL", CF_TYPE_SSL, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, @@ -1721,7 +1717,7 @@ struct Curl_cftype Curl_cft_ssl = { struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", CF_TYPE_SSL, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, diff --git a/vendor/curl/lib/vtls/vtls.h b/vendor/curl/lib/vtls/vtls.h index 3516247301..8ad1cf6def 100644 --- a/vendor/curl/lib/vtls/vtls.h +++ b/vendor/curl/lib/vtls/vtls.h @@ -43,7 +43,7 @@ struct Curl_ssl_session; #define VTLS_INFOF_NO_ALPN \ "ALPN: server did not agree on a protocol. Uses default." #define VTLS_INFOF_ALPN_OFFER_1STR \ - "ALPN: offers %s" + "ALPN: curl offers %s" #define VTLS_INFOF_ALPN_ACCEPTED_1STR \ ALPN_ACCEPTED "%s" #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ diff --git a/vendor/curl/lib/vtls/vtls_int.h b/vendor/curl/lib/vtls/vtls_int.h index ed49339e47..a6e4544a87 100644 --- a/vendor/curl/lib/vtls/vtls_int.h +++ b/vendor/curl/lib/vtls/vtls_int.h @@ -73,7 +73,7 @@ struct ssl_connect_data { char *hostname; /* hostname for verification */ char *dispname; /* display version of hostname */ const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ - struct ssl_backend_data *backend; /* vtls backend specific props */ + void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ int port; /* remote port at origin */ @@ -81,6 +81,7 @@ struct ssl_connect_data { }; +#undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) \ ((struct ssl_connect_data *)(cf)->ctx)->call_data @@ -216,8 +217,6 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, #include "openssl.h" /* OpenSSL versions */ #include "gtls.h" /* GnuTLS versions */ -#include "nssg.h" /* NSS versions */ -#include "gskit.h" /* Global Secure ToolKit versions */ #include "wolfssl.h" /* wolfSSL versions */ #include "schannel.h" /* Schannel SSPI version */ #include "sectransp.h" /* SecureTransport (Darwin) version */ diff --git a/vendor/curl/lib/vtls/wolfssl.c b/vendor/curl/lib/vtls/wolfssl.c index 292872878c..5f15720742 100644 --- a/vendor/curl/lib/vtls/wolfssl.c +++ b/vendor/curl/lib/vtls/wolfssl.c @@ -91,10 +91,10 @@ #undef USE_BIO_CHAIN #endif -struct ssl_backend_data { - SSL_CTX* ctx; - SSL* handle; - CURLcode io_result; /* result of last BIO cfilter operation */ +struct wolfssl_ssl_backend_data { + WOLFSSL_CTX *ctx; + WOLFSSL *handle; + CURLcode io_result; /* result of last BIO cfilter operation */ }; #ifdef OPENSSL_EXTRA @@ -180,7 +180,8 @@ wolfssl_log_tls12_secret(SSL *ssl) } #endif - if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) { + if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != + SSL_SUCCESS) { return; } @@ -281,18 +282,20 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_OK; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - connssl->backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d", - blen, nwritten, result)); + backend->io_result = result; + CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", + blen, nwritten, result); wolfSSL_BIO_clear_retry_flags(bio); if(nwritten < 0 && CURLE_AGAIN == result) - BIO_set_retry_read(bio); + BIO_set_retry_write(bio); return (int)nwritten; } @@ -300,6 +303,8 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_OK; @@ -310,9 +315,8 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - connssl->backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d", - blen, nread, result)); + backend->io_result = result; + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); wolfSSL_BIO_clear_retry_flags(bio); if(nread < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -323,17 +327,17 @@ static WOLFSSL_BIO_METHOD *bio_cf_method = NULL; static void bio_cf_init_methods(void) { - bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); - wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); - wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); - wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); - wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); - wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); + bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); + wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); + wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); + wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); + wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); } static void bio_cf_free_methods(void) { - wolfSSL_BIO_meth_free(bio_cf_method); + wolfSSL_BIO_meth_free(bio_cf_method); } #else /* USE_BIO_CHAIN */ @@ -352,10 +356,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { char *ciphers, *curves; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - SSL_METHOD* req_method = NULL; + WOLFSSL_METHOD* req_method = NULL; #ifdef HAVE_LIBOQS word16 oqsAlg = 0; size_t idx = 0; @@ -366,6 +372,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #else #define use_sni(x) Curl_nop_stmt #endif + bool imported_native_ca = false; + bool imported_ca_info_blob = false; DEBUGASSERT(backend); @@ -410,8 +418,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif break; case CURL_SSLVERSION_TLSv1_2: +#ifndef WOLFSSL_NO_TLS12 req_method = TLSv1_2_client_method(); use_sni(TRUE); +#else + failf(data, "wolfSSL does not support TLS 1.2"); + return CURLE_NOT_BUILT_IN; +#endif break; case CURL_SSLVERSION_TLSv1_3: #ifdef WOLFSSL_TLS13 @@ -433,8 +446,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } if(backend->ctx) - SSL_CTX_free(backend->ctx); - backend->ctx = SSL_CTX_new(req_method); + wolfSSL_CTX_free(backend->ctx); + backend->ctx = wolfSSL_CTX_new(req_method); if(!backend->ctx) { failf(data, "SSL: couldn't create a context"); @@ -494,13 +507,47 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } } + +#ifndef NO_FILESYSTEM + /* load native CA certificates */ + if(ssl_config->native_ca_store) { + if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) { + infof(data, "error importing native CA store, continuing anyway"); + } + else { + imported_native_ca = true; + infof(data, "successfully imported native CA store"); + } + } +#endif /* !NO_FILESYSTEM */ + + /* load certificate blob */ + if(ca_info_blob) { + if(wolfSSL_CTX_load_verify_buffer(backend->ctx, ca_info_blob->data, + ca_info_blob->len, + SSL_FILETYPE_PEM) != SSL_SUCCESS) { + if(imported_native_ca) { + infof(data, "error importing CA certificate blob, continuing anyway"); + } + else { + failf(data, "error importing CA certificate blob"); + return CURLE_SSL_CACERT_BADFILE; + } + } + else { + imported_ca_info_blob = true; + infof(data, "successfully imported CA certificate blob"); + } + } + #ifndef NO_FILESYSTEM /* load trusted cacert */ if(conn_config->CAfile) { - if(1 != SSL_CTX_load_verify_locations(backend->ctx, - conn_config->CAfile, - conn_config->CApath)) { - if(conn_config->verifypeer) { + if(1 != wolfSSL_CTX_load_verify_locations(backend->ctx, + conn_config->CAfile, + conn_config->CApath)) { + if(conn_config->verifypeer && !imported_ca_info_blob && + !imported_native_ca) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:" " CAfile: %s CApath: %s", @@ -531,17 +578,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ssl_config->primary.clientcert && ssl_config->key) { int file_type = do_file_type(ssl_config->cert_type); - if(SSL_CTX_use_certificate_file(backend->ctx, - ssl_config->primary.clientcert, - file_type) != 1) { + if(wolfSSL_CTX_use_certificate_file(backend->ctx, + ssl_config->primary.clientcert, + file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(ssl_config->key_type); - if(SSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, - file_type) != 1) { + if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, + file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } @@ -552,10 +599,9 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, - conn_config->verifypeer?SSL_VERIFY_PEER: - SSL_VERIFY_NONE, - NULL); + wolfSSL_CTX_set_verify(backend->ctx, + conn_config->verifypeer?SSL_VERIFY_PEER: + SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(sni) { @@ -604,8 +650,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Let's make an SSL structure */ if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); + wolfSSL_free(backend->handle); + backend->handle = wolfSSL_new(backend->ctx); if(!backend->handle) { failf(data, "SSL: couldn't create a handle"); return CURLE_OUT_OF_MEMORY; @@ -665,7 +711,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) infof(data, "Can't use session ID, going on without"); } else - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -683,7 +729,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #else /* USE_BIO_CHAIN */ /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) { + if(!wolfSSL_set_fd(backend->handle, + (int)Curl_conn_cf_get_socket(cf, data))) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -699,7 +746,8 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { int ret = -1; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: @@ -707,7 +755,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); /* Enable RFC2818 checks */ if(conn_config->verifyhost) { @@ -717,7 +765,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } - ret = SSL_connect(backend->handle); + ret = wolfSSL_connect(backend->handle); #ifdef OPENSSL_EXTRA if(Curl_tls_keylog_enabled()) { @@ -745,7 +793,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret != 1) { char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int detail = SSL_get_error(backend->handle, ret); + int detail = wolfSSL_get_error(backend->handle, ret); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -803,7 +851,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } else { failf(data, "SSL_connect failed with error %d: %s", detail, - ERR_error_string(detail, error_buffer)); + wolfSSL_ERR_error_string(detail, error_buffer)); return CURLE_SSL_CONNECT_ERROR; } } @@ -817,7 +865,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_asn1Element *pubkey; CURLcode result; - x509 = SSL_get_peer_certificate(backend->handle); + x509 = wolfSSL_get_peer_certificate(backend->handle); if(!x509) { failf(data, "SSL: failed retrieving server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; @@ -892,7 +940,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -902,8 +951,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) bool incache; bool added = FALSE; void *old_ssl_sessionid = NULL; - /* SSL_get1_session allocates memory that has to be freed. */ - SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle); + /* wolfSSL_get1_session allocates memory that has to be freed. */ + WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); if(our_ssl_sessionid) { Curl_ssl_sessionid_lock(data); @@ -920,7 +969,7 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); if(result) { Curl_ssl_sessionid_unlock(data); - SSL_SESSION_free(our_ssl_sessionid); + wolfSSL_SESSION_free(our_ssl_sessionid); failf(data, "failed to store ssl session"); return result; } @@ -932,7 +981,7 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) if(!added) { /* If the session info wasn't added to the cache, free our copy. */ - SSL_SESSION_free(our_ssl_sessionid); + wolfSSL_SESSION_free(our_ssl_sessionid); } } } @@ -950,49 +999,50 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); - rc = SSL_write(backend->handle, mem, memlen); + rc = wolfSSL_write(backend->handle, mem, memlen); if(rc <= 0) { - int err = SSL_get_error(backend->handle, rc); + int err = wolfSSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_write() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; default: if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", - len, rc, err)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err); failf(data, "SSL write: %s, errno %d", - ERR_error_string(err, error_buffer), + wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_SEND_ERROR; return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc); return rc; } static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; (void) data; @@ -1002,13 +1052,13 @@ static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) char buf[32]; /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)SSL_shutdown(backend->handle); - SSL_free(backend->handle); + (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf)); + (void)wolfSSL_shutdown(backend->handle); + wolfSSL_free(backend->handle); backend->handle = NULL; } if(backend->ctx) { - SSL_CTX_free(backend->ctx); + wolfSSL_CTX_free(backend->ctx); backend->ctx = NULL; } } @@ -1019,24 +1069,25 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int nread; DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); *curlcode = CURLE_OK; - nread = SSL_read(backend->handle, buf, buffsize); + nread = wolfSSL_read(backend->handle, buf, buffsize); if(nread <= 0) { - int err = SSL_get_error(backend->handle, nread); + int err = wolfSSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); *curlcode = CURLE_OK; return 0; case SSL_ERROR_NONE: @@ -1044,30 +1095,30 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, case SSL_ERROR_WANT_READ: /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); + /* there's data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; default: if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; } failf(data, "SSL read: %s, errno %d", - ERR_error_string(err, error_buffer), SOCKERRNO); + wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_RECV_ERROR; return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread); return nread; } static void wolfssl_session_free(void *ptr) { - SSL_SESSION_free(ptr); + wolfSSL_SESSION_free(ptr); } @@ -1108,11 +1159,14 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct wolfssl_ssl_backend_data *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - if(ctx->backend->handle) /* SSL is in use */ - return (0 != SSL_pending(ctx->backend->handle)) ? TRUE : FALSE; + + backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + if(backend->handle) /* SSL is in use */ + return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; else return FALSE; } @@ -1126,15 +1180,17 @@ static int wolfssl_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; + struct wolfssl_ssl_backend_data *backend; int retval = 0; (void)data; DEBUGASSERT(ctx && ctx->backend); - if(ctx->backend->handle) { - ERR_clear_error(); - SSL_free(ctx->backend->handle); - ctx->backend->handle = NULL; + backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + if(backend->handle) { + wolfSSL_ERR_clear_error(); + wolfSSL_free(backend->handle); + backend->handle = NULL; } return retval; } @@ -1296,7 +1352,8 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ { wc_Sha256 SHA256pw; (void)unused; - wc_InitSha256(&SHA256pw); + if(wc_InitSha256(&SHA256pw)) + return CURLE_FAILED_INIT; wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); wc_Sha256Final(&SHA256pw, sha256sum); return CURLE_OK; @@ -1305,7 +1362,8 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct ssl_backend_data *backend = connssl->backend; + struct wolfssl_ssl_backend_data *backend = + (struct wolfssl_ssl_backend_data *)connssl->backend; (void)info; DEBUGASSERT(backend); return backend->handle; @@ -1320,9 +1378,10 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #ifdef USE_BIO_CHAIN SSLSUPP_HTTPS_PROXY | #endif + SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, - sizeof(struct ssl_backend_data), + sizeof(struct wolfssl_ssl_backend_data), wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ diff --git a/vendor/curl/lib/vtls/x509asn1.c b/vendor/curl/lib/vtls/x509asn1.c index acf8bdb2ab..c3fd3a30bb 100644 --- a/vendor/curl/lib/vtls/x509asn1.c +++ b/vendor/curl/lib/vtls/x509asn1.c @@ -24,24 +24,18 @@ #include "curl_setup.h" -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) -#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) +#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL) #define WANT_PARSEX509 /* uses Curl_parseX509() */ #endif -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #define WANT_PARSEX509 /* ... uses Curl_parseX509() */ #endif -#if defined(USE_GSKIT) -#define WANT_VERIFYHOST /* uses Curl_verifyhost () */ -#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ -#endif - #include #include "urldata.h" #include "strcase.h" @@ -973,7 +967,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, infof(data, " ECC Public Key (%lu bits)", len); if(data->set.ssl.certinfo) { char q[sizeof(len) * 8 / 3 + 1]; - (void)msnprintf(q, sizeof(q), "%lu", len); + (void)msnprintf(q, sizeof(q), "%zu", len); if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; } @@ -1007,7 +1001,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, infof(data, " RSA Public Key (%lu bits)", len); if(data->set.ssl.certinfo) { char r[sizeof(len) * 8 / 3 + 1]; - msnprintf(r, sizeof(r), "%lu", len); + msnprintf(r, sizeof(r), "%zu", len); if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) return 1; } @@ -1261,8 +1255,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, #endif /* WANT_EXTRACT_CERTINFO */ -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ #ifdef WANT_VERIFYHOST diff --git a/vendor/curl/lib/vtls/x509asn1.h b/vendor/curl/lib/vtls/x509asn1.h index 5496de40e4..23a67b828a 100644 --- a/vendor/curl/lib/vtls/x509asn1.h +++ b/vendor/curl/lib/vtls/x509asn1.h @@ -27,8 +27,8 @@ #include "curl_setup.h" -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) #include "cfilters.h" #include "urldata.h" @@ -76,6 +76,5 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, const char *beg, const char *end); CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ #endif /* HEADER_CURL_X509ASN1_H */ diff --git a/vendor/curl/lib/warnless.c b/vendor/curl/lib/warnless.c index 10c91fb28f..65c5ec5354 100644 --- a/vendor/curl/lib/warnless.c +++ b/vendor/curl/lib/warnless.c @@ -35,10 +35,13 @@ #endif /* __INTEL_COMPILER && __unix__ */ -#define BUILDING_WARNLESS_C 1 - #include "warnless.h" +#ifdef WIN32 +#undef read +#undef write +#endif + #include #define CURL_MASK_UCHAR ((unsigned char)~0) @@ -376,6 +379,9 @@ ssize_t curlx_write(int fd, const void *buf, size_t count) return (ssize_t)write(fd, buf, curlx_uztoui(count)); } +/* Ensure that warnless.h continues to have an effect in "unity" builds. */ +#undef HEADER_CURL_WARNLESS_H + #endif /* WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) diff --git a/vendor/curl/lib/warnless.h b/vendor/curl/lib/warnless.h index 99b243379e..2a5301628f 100644 --- a/vendor/curl/lib/warnless.h +++ b/vendor/curl/lib/warnless.h @@ -75,12 +75,10 @@ ssize_t curlx_read(int fd, void *buf, size_t count); ssize_t curlx_write(int fd, const void *buf, size_t count); -#ifndef BUILDING_WARNLESS_C -# undef read -# define read(fd, buf, count) curlx_read(fd, buf, count) -# undef write -# define write(fd, buf, count) curlx_write(fd, buf, count) -#endif +#undef read +#define read(fd, buf, count) curlx_read(fd, buf, count) +#undef write +#define write(fd, buf, count) curlx_write(fd, buf, count) #endif /* WIN32 */ diff --git a/vendor/curl/lib/ws.c b/vendor/curl/lib/ws.c index c60bbc95b3..3c1964b860 100644 --- a/vendor/curl/lib/ws.c +++ b/vendor/curl/lib/ws.c @@ -126,8 +126,9 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, dec->head_len, dec->head_total); } else { - infof(data, "WS-DEC: %s [%s%s payload=%zd/%zd]", msg, - ws_frame_name_of_op(dec->head[0]), + infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T + "/%" CURL_FORMAT_CURL_OFF_T "]", + msg, ws_frame_name_of_op(dec->head[0]), (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", dec->payload_offset, dec->payload_len); } @@ -272,7 +273,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, Curl_bufq_skip(inraw, (size_t)nwritten); dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; - /* infof(data, "WS-DEC: passed %zd bytes payload, %zd remain", + /* infof(data, "WS-DEC: passed %zd bytes payload, %" + CURL_FORMAT_CURL_OFF_T " remain", nwritten, remain); */ } @@ -351,8 +353,9 @@ static void update_meta(struct websocket *ws, static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, const char *msg) { - infof(data, "WS-ENC: %s [%s%s%s payload=%zd/%zd]", msg, - ws_frame_name_of_op(enc->firstbyte), + infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T + "/%" CURL_FORMAT_CURL_OFF_T "]", + msg, ws_frame_name_of_op(enc->firstbyte), (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ? " CONT" : "", (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN", @@ -839,7 +842,7 @@ static ssize_t nw_in_recv(void *reader_ctx, CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, size_t buflen, size_t *nread, - struct curl_ws_frame **metap) + const struct curl_ws_frame **metap) { struct connectdata *conn = data->conn; struct websocket *ws; @@ -921,7 +924,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, ctx.payload_len, ctx.bufidx); *metap = &ws->frame; *nread = ws->frame.len; - /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %zd, %zd left)", + /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" + CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ return CURLE_OK; } @@ -966,10 +970,10 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return CURLE_OK; } -CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, size_t buflen, size_t *sent, - curl_off_t totalsize, - unsigned int sendflags) + curl_off_t fragsize, + unsigned int flags) { struct websocket *ws; ssize_t nwritten, n; @@ -987,14 +991,13 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, return CURLE_SEND_ERROR; } if(!data->conn->proto.ws) { - failf(data, "Not a websocket transfer on connection #%ld", - data->conn->connection_id); + failf(data, "Not a websocket transfer"); return CURLE_SEND_ERROR; } ws = data->conn->proto.ws; if(data->set.ws_raw_mode) { - if(totalsize || sendflags) + if(fragsize || flags) return CURLE_BAD_FUNCTION_ARGUMENT; if(!buflen) /* nothing to do */ @@ -1027,23 +1030,24 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, if(space < 14) return CURLE_AGAIN; - if(sendflags & CURLWS_OFFSET) { - if(totalsize) { - /* a frame series 'totalsize' bytes big, this is the first */ - n = ws_enc_write_head(data, &ws->enc, sendflags, totalsize, + if(flags & CURLWS_OFFSET) { + if(fragsize) { + /* a frame series 'fragsize' bytes big, this is the first */ + n = ws_enc_write_head(data, &ws->enc, flags, fragsize, &ws->sendbuf, &result); if(n < 0) return result; } else { if((curl_off_t)buflen > ws->enc.payload_remain) { - infof(data, "WS: unaligned frame size (sending %zu instead of %zd)", + infof(data, "WS: unaligned frame size (sending %zu instead of %" + CURL_FORMAT_CURL_OFF_T ")", buflen, ws->enc.payload_remain); } } } else if(!ws->enc.payload_remain) { - n = ws_enc_write_head(data, &ws->enc, sendflags, (curl_off_t)buflen, + n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, &ws->sendbuf, &result); if(n < 0) return result; @@ -1082,7 +1086,7 @@ CURLcode Curl_ws_disconnect(struct Curl_easy *data, return CURLE_OK; } -CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) { /* we only return something for websocket, called from within the callback when not using raw mode */ @@ -1096,7 +1100,7 @@ CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *nread, - struct curl_ws_frame **metap) + const struct curl_ws_frame **metap) { (void)curl; (void)buffer; @@ -1108,19 +1112,19 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, size_t buflen, size_t *sent, - curl_off_t framesize, - unsigned int sendflags) + curl_off_t fragsize, + unsigned int flags) { (void)curl; (void)buffer; (void)buflen; (void)sent; - (void)framesize; - (void)sendflags; + (void)fragsize; + (void)flags; return CURLE_NOT_BUILT_IN; } -CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) { (void)data; return NULL; diff --git a/vendor/curl/premake5.lua b/vendor/curl/premake5.lua index ff5f013da6..b3ac6a517f 100644 --- a/vendor/curl/premake5.lua +++ b/vendor/curl/premake5.lua @@ -11,7 +11,8 @@ project "curl" files { "premake5.lua", "include/**.h", - "lib/**.c" + "lib/**.c", + "lib/**.h" } removefiles { "lib/amigaos.c",