summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordartraiden <wowemuh@gmail.com>2024-01-31 18:58:27 +0300
committerdartraiden <wowemuh@gmail.com>2024-01-31 19:00:01 +0300
commit1e6eb7b2eec5520b510b4437a6f13578f0acddc6 (patch)
tree01f0471822749ef254c71680b9aec022a7765260
parentd4ca709d1c304a6d831feff16d9551015c66dde5 (diff)
libcurl: update to 8.6.0
-rw-r--r--libs/libcurl/docs/CHANGES5108
-rw-r--r--libs/libcurl/docs/COPYING2
-rw-r--r--libs/libcurl/docs/THANKS39
-rw-r--r--libs/libcurl/include/curl/curl.h10
-rw-r--r--libs/libcurl/include/curl/curlver.h8
-rw-r--r--libs/libcurl/include/curl/mprintf.h18
-rw-r--r--libs/libcurl/include/curl/system.h12
-rw-r--r--libs/libcurl/include/curl/urlapi.h1
-rw-r--r--libs/libcurl/src/Makefile.in132
-rw-r--r--libs/libcurl/src/Makefile.inc8
-rw-r--r--libs/libcurl/src/altsvc.c11
-rw-r--r--libs/libcurl/src/asyn-ares.c156
-rw-r--r--libs/libcurl/src/asyn-thread.c265
-rw-r--r--libs/libcurl/src/bufref.c6
-rw-r--r--libs/libcurl/src/c-hyper.c5
-rw-r--r--libs/libcurl/src/cf-h1-proxy.c75
-rw-r--r--libs/libcurl/src/cf-h2-proxy.c12
-rw-r--r--libs/libcurl/src/cf-haproxy.c4
-rw-r--r--libs/libcurl/src/cf-https-connect.c2
-rw-r--r--libs/libcurl/src/cf-socket.c91
-rw-r--r--libs/libcurl/src/cf-socket.h17
-rw-r--r--libs/libcurl/src/cfilters.c14
-rw-r--r--libs/libcurl/src/cfilters.h5
-rw-r--r--libs/libcurl/src/config-os400.h6
-rw-r--r--libs/libcurl/src/config-win32.h62
-rw-r--r--libs/libcurl/src/config-win32ce.h9
-rw-r--r--libs/libcurl/src/connect.c70
-rw-r--r--libs/libcurl/src/content_encoding.c54
-rw-r--r--libs/libcurl/src/cookie.c10
-rw-r--r--libs/libcurl/src/curl_config.h.cmake18
-rw-r--r--libs/libcurl/src/curl_config.h.in34
-rw-r--r--libs/libcurl/src/curl_ntlm_wb.c6
-rw-r--r--libs/libcurl/src/curl_printf.h4
-rw-r--r--libs/libcurl/src/curl_rtmp.c12
-rw-r--r--libs/libcurl/src/curl_sasl.c19
-rw-r--r--libs/libcurl/src/curl_setup.h86
-rw-r--r--libs/libcurl/src/curl_trc.c2
-rw-r--r--libs/libcurl/src/curl_trc.h25
-rw-r--r--libs/libcurl/src/dict.c5
-rw-r--r--libs/libcurl/src/doh.c27
-rw-r--r--libs/libcurl/src/dynbuf.c7
-rw-r--r--libs/libcurl/src/dynbuf.h4
-rw-r--r--libs/libcurl/src/easy.c52
-rw-r--r--libs/libcurl/src/easyoptions.c4
-rw-r--r--libs/libcurl/src/file.c38
-rw-r--r--libs/libcurl/src/formdata.c10
-rw-r--r--libs/libcurl/src/ftp.c199
-rw-r--r--libs/libcurl/src/getinfo.c5
-rw-r--r--libs/libcurl/src/gopher.c4
-rw-r--r--libs/libcurl/src/headers.c35
-rw-r--r--libs/libcurl/src/hostasyn.c11
-rw-r--r--libs/libcurl/src/hostip.c24
-rw-r--r--libs/libcurl/src/hsts.c34
-rw-r--r--libs/libcurl/src/http.c333
-rw-r--r--libs/libcurl/src/http.h24
-rw-r--r--libs/libcurl/src/http2.c72
-rw-r--r--libs/libcurl/src/http_aws_sigv4.c19
-rw-r--r--libs/libcurl/src/http_chunks.c294
-rw-r--r--libs/libcurl/src/http_chunks.h60
-rw-r--r--libs/libcurl/src/http_proxy.c4
-rw-r--r--libs/libcurl/src/imap.c93
-rw-r--r--libs/libcurl/src/inet_pton.c3
-rw-r--r--libs/libcurl/src/inet_pton.h3
-rw-r--r--libs/libcurl/src/krb5.c39
-rw-r--r--libs/libcurl/src/ldap.c6
-rw-r--r--libs/libcurl/src/libcurl.plist6
-rw-r--r--libs/libcurl/src/md4.c6
-rw-r--r--libs/libcurl/src/memdebug.c37
-rw-r--r--libs/libcurl/src/memdebug.h2
-rw-r--r--libs/libcurl/src/mime.c21
-rw-r--r--libs/libcurl/src/mime.h3
-rw-r--r--libs/libcurl/src/mprintf.c1203
-rw-r--r--libs/libcurl/src/mqtt.c29
-rw-r--r--libs/libcurl/src/mqtt.h1
-rw-r--r--libs/libcurl/src/multi.c146
-rw-r--r--libs/libcurl/src/noproxy.c1
-rw-r--r--libs/libcurl/src/openldap.c27
-rw-r--r--libs/libcurl/src/pingpong.c268
-rw-r--r--libs/libcurl/src/pingpong.h20
-rw-r--r--libs/libcurl/src/pop3.c106
-rw-r--r--libs/libcurl/src/progress.c10
-rw-r--r--libs/libcurl/src/progress.h3
-rw-r--r--libs/libcurl/src/rand.c6
-rw-r--r--libs/libcurl/src/rtsp.c177
-rw-r--r--libs/libcurl/src/sendf.c85
-rw-r--r--libs/libcurl/src/sendf.h4
-rw-r--r--libs/libcurl/src/setopt.c44
-rw-r--r--libs/libcurl/src/setup-win32.h62
-rw-r--r--libs/libcurl/src/share.c8
-rw-r--r--libs/libcurl/src/smb.c4
-rw-r--r--libs/libcurl/src/smtp.c32
-rw-r--r--libs/libcurl/src/socketpair.c3
-rw-r--r--libs/libcurl/src/socks.c79
-rw-r--r--libs/libcurl/src/socks_gssapi.c7
-rw-r--r--libs/libcurl/src/socks_sspi.c12
-rw-r--r--libs/libcurl/src/strdup.c12
-rw-r--r--libs/libcurl/src/strdup.h2
-rw-r--r--libs/libcurl/src/strerror.c58
-rw-r--r--libs/libcurl/src/system_win32.c31
-rw-r--r--libs/libcurl/src/system_win32.h30
-rw-r--r--libs/libcurl/src/telnet.c117
-rw-r--r--libs/libcurl/src/tftp.c2
-rw-r--r--libs/libcurl/src/transfer.c503
-rw-r--r--libs/libcurl/src/transfer.h21
-rw-r--r--libs/libcurl/src/url.c62
-rw-r--r--libs/libcurl/src/urlapi.c169
-rw-r--r--libs/libcurl/src/urldata.h36
-rw-r--r--libs/libcurl/src/vauth/digest_sspi.c4
-rw-r--r--libs/libcurl/src/vauth/krb5_gssapi.c3
-rw-r--r--libs/libcurl/src/vauth/krb5_sspi.c3
-rw-r--r--libs/libcurl/src/vauth/ntlm.c6
-rw-r--r--libs/libcurl/src/vauth/ntlm_sspi.c8
-rw-r--r--libs/libcurl/src/version.c8
-rw-r--r--libs/libcurl/src/vquic/curl_msh3.c8
-rw-r--r--libs/libcurl/src/vquic/curl_ngtcp2.c573
-rw-r--r--libs/libcurl/src/vquic/curl_osslq.c2237
-rw-r--r--libs/libcurl/src/vquic/curl_osslq.h51
-rw-r--r--libs/libcurl/src/vquic/curl_quiche.c297
-rw-r--r--libs/libcurl/src/vquic/vquic-tls.c609
-rw-r--r--libs/libcurl/src/vquic/vquic-tls.h98
-rw-r--r--libs/libcurl/src/vquic/vquic.c16
-rw-r--r--libs/libcurl/src/vquic/vquic_int.h4
-rw-r--r--libs/libcurl/src/vssh/libssh.c90
-rw-r--r--libs/libcurl/src/vssh/libssh2.c92
-rw-r--r--libs/libcurl/src/vssh/wolfssh.c28
-rw-r--r--libs/libcurl/src/vtls/bearssl.c32
-rw-r--r--libs/libcurl/src/vtls/gtls.c10
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c31
-rw-r--r--libs/libcurl/src/vtls/openssl.c52
-rw-r--r--libs/libcurl/src/vtls/rustls.c12
-rw-r--r--libs/libcurl/src/vtls/schannel.c58
-rw-r--r--libs/libcurl/src/vtls/schannel_verify.c4
-rw-r--r--libs/libcurl/src/vtls/sectransp.c16
-rw-r--r--libs/libcurl/src/vtls/vtls.c92
-rw-r--r--libs/libcurl/src/vtls/vtls.h2
-rw-r--r--libs/libcurl/src/vtls/wolfssl.c27
-rw-r--r--libs/libcurl/src/vtls/x509asn1.c899
-rw-r--r--libs/libcurl/src/ws.c336
-rw-r--r--libs/libcurl/src/ws.h13
139 files changed, 10108 insertions, 6893 deletions
diff --git a/libs/libcurl/docs/CHANGES b/libs/libcurl/docs/CHANGES
index 85fa4522fb..8e70215477 100644
--- a/libs/libcurl/docs/CHANGES
+++ b/libs/libcurl/docs/CHANGES
@@ -6,6 +6,2721 @@
Changelog
+Version 8.6.0 (31 Jan 2024)
+
+Daniel Stenberg (31 Jan 2024)
+
+- RELEASE-NOTES: synced
+
+ curl 8.6.0
+
+- THANKS: new contributors from 8.5.0
+
+Jay Satiro (31 Jan 2024)
+
+- cd2nroff: use perl 'strict' and 'warnings'
+
+ - Use strict and warnings pragmas.
+
+ - If open() fails then show the reason.
+
+ - Set STDIN io layer :crlf so that input is properly read on Windows.
+
+ - When STDIN is used as input, the filename $f is now set to "STDIN".
+
+ Various error messages in single() use $f for the filename and this way
+ it is not undefined when STDIN.
+
+ Closes https://github.com/curl/curl/pull/12819
+
+Daniel Stenberg (30 Jan 2024)
+
+- cd2nroff: fix duplicate output issue
+
+ Assisted-by: Jay Satiro
+ Fixes https://github.com/curl/curl-www/issues/321
+ Closes #12818
+
+- lib: error out on multissl + http3
+
+ Since the QUIC/h3 code has no knowledge or handling of multissl it might
+ bring unintended consequences if we allow it.
+
+ configure, cmake and curl_setup.h all now reject this combination.
+
+ Assisted-by: Viktor Szakats
+ Assisted-by: Gisle Vanem
+ Ref: #12806
+ Closes #12807
+
+Patrick Monnerat (29 Jan 2024)
+
+- OS400: sync ILE/RPG binding
+
+ Also do not force git CRLF line endings on *.cmd files for OS400.
+
+ Closes #12815
+
+Viktor Szakats (28 Jan 2024)
+
+- build: delete/replace 3 more clang warning pragmas
+
+ - tool_msgs: delete redundant `-Wformat-nonliteral` suppression pragma.
+
+ - whitespace formatting in `mprintf.h`, lib518, lib537.
+
+ - lib518: fix wrong variable in `sizeof()`.
+
+ - lib518: bump variables to `rlim_t`.
+ Follow-up to e2b394106d543c4615a60795b7fdce04bd4e5090 #1469
+
+ - lib518: sync error message with lib537
+ Follow-up to 365322b8bcf9efb6a361473d227b70f2032212ce
+
+ - lib518, lib537: replace `-Wformat-nonliteral` suppression pragmas
+ by reworking test code.
+
+ Follow-up to 5b286c250829e06a135a6ba998e80beb7f43a734 #12812
+ Follow-up to aee4ebe59161d0a5281743f96e7738ad97fe1cd4 #12803
+ Follow-up to 09230127589eccc7e01c1a7217787ef8e64f3328 #12540
+ Follow-up to 3829759bd042c03225ae862062560f568ba1a231 #12489
+
+ Reviewed-by: Daniel Stenberg
+ Closes #12814
+
+Richard Levitte (27 Jan 2024)
+
+- cmake: freshen up docs/INSTALL.cmake
+
+ - Turn docs/INSTALL.cmake into a proper markdown file,
+ docs/INSTALL-CMAKE.md
+ - Move things around to divide the description into configuration,
+ building and installing sections
+ - Mention the more modern cmake options to configure, build and install,
+ but also retain the older variants as fallbacks
+
+ Closes #12772
+
+Viktor Szakats (27 Jan 2024)
+
+- build: delete/replace clang warning pragmas
+
+ - delete redundant warning suppressions for `-Wformat-nonliteral`.
+ This now relies on `CURL_PRINTF()` and it's theoratically possible
+ that this macro isn't active but the warning is. We're ignoring this
+ as a corner-case here.
+
+ - replace two pragmas with code changes to avoid the warnings.
+
+ Follow-up to aee4ebe59161d0a5281743f96e7738ad97fe1cd4 #12803
+ Follow-up to 09230127589eccc7e01c1a7217787ef8e64f3328 #12540
+ Follow-up to 3829759bd042c03225ae862062560f568ba1a231 #12489
+
+ Reviewed-by: Daniel Stenberg
+ Closes #12812
+
+Daniel Stenberg (27 Jan 2024)
+
+- RELEASE-NOTES: synced
+
+- http: only act on 101 responses when they are HTTP/1.1
+
+ For 101 responses claiming to be any other protocol, bail out. This
+ would previously trigger an assert.
+
+ Add test 1704 to verify.
+
+ Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=66184
+ Closes #12811
+
+Scarlett McAllister (27 Jan 2024)
+
+- _VARIABLES.md: add missing 'be' into the sentence
+
+ Closes #12809
+
+Stefan Eissing (27 Jan 2024)
+
+- mqtt, remove remaining use of data->state.buffer
+
+ Closes #12799
+
+Daniel Stenberg (27 Jan 2024)
+
+- x509asn1: switch from malloc to dynbuf
+
+ Closes #12808
+
+- x509asn1: make utf8asn1str() use dynbuf instead of malloc + memcpy
+
+ Closes #12808
+
+- x509asn1: reduce malloc in Curl_extract_certinfo
+
+ Using dynbuf
+
+ Closes #12808
+
+Jay Satiro (27 Jan 2024)
+
+- THANKS: add Alexander Bartel and Brennan Kinney
+
+ They reported and investigated #10259 which was fixed by 7b2d98df.
+
+ Ref: https://github.com/curl/curl/issues/10259
+
+Daniel Stenberg (26 Jan 2024)
+
+- krb5: add prototype to silence clang warnings on mvsnprintf()
+
+ "error: format string is not a string literal"
+
+ Follow-up to 09230127589eccc7 which made the warning appear
+
+ Assisted-by: Viktor Szakats
+ Closes #12803
+
+- x509asn1: remove code for WANT_VERIFYHOST
+
+ No code ever sets this anymore since we dropped gskit
+
+ Follow-up to 78d6232f1f326b9ab4d
+
+ Closes #12804
+
+- socks: reduce the buffer size to 600 (from 8K)
+
+ This is malloc'ed memory and it does not more. Test 742 helps us verify
+ this.
+
+ Closes #12789
+
+Stefan Eissing (26 Jan 2024)
+
+- file+ftp: use stack buffers instead of data->state.buffer
+
+ Closes #12789
+
+- vtls: receive max buffer
+
+ - do not only receive one TLS record, but try to fill
+ the passed buffer
+ - consider <4K remaning space is "filled".
+
+ Closes #12801
+
+Daniel Stenberg (26 Jan 2024)
+
+- docs: do not start lines/sentences with So, But nor And
+
+ Closes #12802
+
+- docs: remove spurious ampersands from markdown
+
+ They were leftovers from the nroff conversion.
+
+ Follow-up to eefcc1bda4bccd800f5a5
+
+ Closes #12800
+
+Patrick Monnerat (26 Jan 2024)
+
+- sasl: make login option string override http auth
+
+ - Use http authentication mechanisms as a default, not a preset.
+
+ Consider http authentication options which are mapped to SASL options as
+ a default (overriding the hardcoded default mask for the protocol) that
+ is ignored if a login option string is given.
+
+ Prior to this change, if some HTTP auth options were given, sasl mapped
+ http authentication options to sasl ones but merged them with the login
+ options.
+
+ That caused problems with the cli tool that sets the http login option
+ CURLAUTH_BEARER as a side-effect of --oauth2-bearer, because this flag
+ maps to more than one sasl mechanisms and the latter cannot be cleared
+ individually by the login options string.
+
+ New test 992 checks this.
+
+ Fixes https://github.com/curl/curl/issues/10259
+ Closes https://github.com/curl/curl/pull/12790
+
+Stefan Eissing (26 Jan 2024)
+
+- socks: use own buffer instead of data->state.buffer
+
+ Closes #12788
+
+Daniel Stenberg (26 Jan 2024)
+
+- socks: fix generic output string to say SOCKS instead of SOCKS4
+
+ ... since it was also logged for SOCKS5.
+
+ Closes #12797
+
+- test742: test SOCKS5 with max length user, password and hostname
+
+ Adjusted the socksd server accordingly to allow for configuring that
+ long user name and password.
+
+ Closes #12797
+
+Stefan Eissing (25 Jan 2024)
+
+- ssh: use stack scratch buffer for seeks
+
+ - instead of data->state.buffer
+
+ Closes #12794
+
+Daniel Stenberg (25 Jan 2024)
+
+- krb5: access the response buffer correctly
+
+ As the pingpong code no longer uses the download buffer.
+
+ Folllow-up to c2d973627bab12ab
+ Pointed-out-by: Stefan Eissing
+ Closes #12796
+
+Stefan Eissing (25 Jan 2024)
+
+- mqtt: use stack scratch buffer for recv+publish
+
+ - instead of data->state.buffer
+
+ Closes #12792
+
+- telnet, use stack scratch buffer for do
+
+ - instead of data->state.buffer
+
+ Closes #12793
+
+- http, use stack scratch buffer
+
+ - instead of data->state.buffer
+
+ Closes #12791
+
+- ntlm_wb: do not use data->state.buf any longer
+
+ Closes #12787
+
+- gitignore: the generated `libcurl-symbols.md`
+
+ Closes #12795
+
+Daniel Stenberg (25 Jan 2024)
+
+- tool: fix the listhelp generation command
+
+ The previous command line to generate the tool_listhelp.c source file
+ broke with 2494b8dd5175cee7.
+
+ Make 'make listhelp' invoked in src/ generate it. Also update the
+ comment in the file to mention the right procedure.
+
+ Closes #12786
+
+- http: check for "Host:" case insensitively
+
+ When checking if the user wants to replace the header, the check should
+ be case insensitive.
+
+ Adding test 461 to verify
+
+ Found-by: Dan Fandrich
+ Ref: #12782
+ Closes #12784
+
+Tatsuhiro Tsujikawa (25 Jan 2024)
+
+- configure: add libngtcp2_crypto_boringssl detection
+
+ If OpenSSL is found to be BoringSSL or AWS-LC, and ngtcp2 is requested,
+ try to detect libngtcp2_crypto_boringssl.
+
+ Reported-by: ウさん
+ Fixes #12724
+ Closes #12769
+
+Daniel Stenberg (25 Jan 2024)
+
+- http: remove comment reference to a removed solution
+
+ Follow-up to 58974d25d
+
+ Closes #12785
+
+Stefan Eissing (25 Jan 2024)
+
+- pytest: Scorecard tracking CPU and RSS
+
+ Closes #12765
+
+Graham Campbell (25 Jan 2024)
+
+- GHA: bump ngtcp2, gnutls, mod_h2, quiche
+
+ - ngtcp2 to v1.2.0
+ - gnutls to 3.8.3
+ - mod_h2 to 2.0.26
+ - quiche to 0.20.0
+
+ Closes #12778
+ Closes #12779
+ Closes #12780
+ Closes #12781
+
+Daniel Stenberg (25 Jan 2024)
+
+- ftpserver.pl: send 213 SIZE response without spurious newline
+
+- pingpong: stop using the download buffer
+
+ The pingpong logic now uses its own dynbuf for receiving command
+ response data.
+
+ When the "final" response header for a commanad has been received, that
+ final line is left first in the recvbuf for the protocols to parse at
+ will. If there is additional data behind the final response line, the
+ 'overflow' counter is indicate how many bytes.
+
+ Closes #12757
+
+- gen.pl: remove bold from .IP used for ##
+
+ Reported-by: Viktor Szakats
+ Fixes #12776
+ Closes #12777
+
+Viktor Szakats (24 Jan 2024)
+
+- cmake: rework options to enable curl and libcurl docs
+
+ Rework CMake options for building/using curl tool and libcurl manuals.
+
+ - rename `ENABLE_MANUAL` to `ENABLE_CURL_MANUAL`, meaning:
+ to build man page and built-in manual for curl tool.
+
+ - rename `BUILD_DOCS` to `BUILD_LIBCURL_DOCS`, meaning:
+ to build man pages for libcurl.
+
+ - `BUILD_LIBCURL_DOCS` now works without having to enable
+ `ENABLE_CURL_MANUAL` too.
+
+ - drop support for existing CMake-level `USE_MANUAL` option to avoid
+ confusion. (It used to work with the effect of current
+ `ENABLE_CURL_MANUAL`, but only by accident.)
+
+ Assisted-by: Richard Levitte
+ Ref: #12771
+ Closes #12773
+
+Daniel Stenberg (24 Jan 2024)
+
+- urlapi: remove assert
+
+ This assert triggers wrongly when CURLU_GUESS_SCHEME and
+ CURLU_NO_AUTHORITY are both set and the URL is a single path.
+
+ I think this assert has played out its role. It was introduced in a
+ rather big refactor.
+
+ Follow-up to 4cfa5bcc9a
+
+ Reported-by: promptfuzz_ on hackerone
+ Closes #12775
+
+Patrick Monnerat (24 Jan 2024)
+
+- tests: avoid int/size_t conversion size/sign warnings
+
+ Closes #12768
+
+Daniel Stenberg (24 Jan 2024)
+
+- GHA: add a job scanning for "bad words" in markdown
+
+ This means words, phrases or things we have decided not to use - words that
+ are spelled right according to the dictionary but we want to avoid. In the
+ name of consistency and better documentation.
+
+ Closes #12764
+
+Viktor Szakats (23 Jan 2024)
+
+- cmake: speed up curldown processing, enable by default
+
+ - cmake: enable `BUILD_DOCS` by default (this controls converting and
+ installing `.3` files from `.md` sources)
+
+ - cmake: speed up generating `.3` files by using a single command per
+ directory, instead of a single command per file. This reduces external
+ commands by about a thousand. (There remains some CMake logic kicking
+ in resulting in 500 -one per file- external `-E touch_nocreate` calls.)
+
+ - cd2nroff: add ability to process multiple input files.
+
+ - cd2nroff: add `-k` option to use the source filename to form the
+ output filename. (instead of the default in-file `Title:` line.)
+
+ Follow-up to 3f08d80b2244524646ce86915c585509ac54fb4c
+ Follow-up to ea0b575dab86a3c44dd1d547dc500276266aa382 #12753
+ Follow-up to eefcc1bda4bccd800f5a56a0fe17a2f44a96e88b #12730
+
+ Closes #12762
+
+Richard Levitte (23 Jan 2024)
+
+- docs: install curl.1 with cmake as well
+
+ Closes #12759
+
+Daniel Stenberg (23 Jan 2024)
+
+- osslq: remove the TLS library from the version output
+
+ Since we only support using a single TLS library at any one time, we
+ know that the TLS library for QUIC is the same that is also shown for
+ regular TLS.
+
+ Fixes #12763
+ Reported-by: Viktor Szakats
+ Closes #12767
+
+Stefan Eissing (23 Jan 2024)
+
+- CI: remove unnecessary OpenSSL 3 option `enable-tls1_3`
+
+ .. and switch OpenSSL 3 libdir from lib64 to lib for consistency.
+
+ Closes https://github.com/curl/curl/pull/12758
+
+- GHA: bump nghttp2 version to v1.59.0
+
+ - Switch to v1.59.0 for GHA CI jobs that use a specific nghttp2-version.
+
+ Closes https://github.com/curl/curl/pull/12766
+
+Daniel Stenberg (23 Jan 2024)
+
+- RELEASE-NOTES: synced
+
+- docs/cmdline: change to .md for cmdline docs
+
+ - switch all invidual files documenting command line options into .md,
+ as the documentation is now markdown-looking.
+
+ - made the parser treat 4-space indents as quotes
+
+ - switch to building the curl.1 manpage using the "mainpage.idx" file,
+ which lists the files to include to generate it, instead of using the
+ previous page-footer/headers. Also, those files are now also .md
+ ones, using the same format. I gave them underscore prefixes to make
+ them sort separately:
+ _NAME.md, _SYNOPSIS.md, _DESCRIPTION.md, _URL.md, _GLOBBING.md,
+ _VARIABLES.md, _OUTPUT.md, _PROTOCOLS.md, _PROGRESS.md, _VERSION.md,
+ _OPTIONS.md, _FILES.md, _ENVIRONMENT.md, _PROXYPREFIX.md,
+ _EXITCODES.md, _BUGS.md, _AUTHORS.md, _WWW.md, _SEEALSO.md
+
+ - updated test cases accordingly
+
+ Closes #12751
+
+dependabot[bot] (23 Jan 2024)
+
+- CI: bump actions/cache from 3 to 4
+
+ Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
+ - [Release notes](https://github.com/actions/cache/releases)
+ - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
+ - [Commits](https://github.com/actions/cache/compare/v3...v4)
+
+ ---
+ updated-dependencies:
+ - dependency-name: actions/cache
+ dependency-type: direct:production
+ update-type: version-update:semver-major
+ ...
+
+ Signed-off-by: dependabot[bot] <support@github.com>
+ Closes #12756
+
+Daniel Stenberg (23 Jan 2024)
+
+- openssl: when verifystatus fails, remove session id from cache
+
+ To prevent that it gets used in a subsequent transfer that skips the
+ verifystatus check since that check can't be done when the session id is
+ reused.
+
+ Reported-by: Hiroki Kurosawa
+ Closes #12760
+
+Viktor Szakats (23 Jan 2024)
+
+- cmake: add option to disable building docs
+
+Richard Levitte (23 Jan 2024)
+
+- cmake: use curldown to build man pages
+
+ This throws away the previous HTML and PDF producers, to mimic what
+ Makefile.am does as faithfully as possible.
+
+ Closes #12753
+
+Daniel Stenberg (23 Jan 2024)
+
+- mksymbolsmanpage.pl: provide references to where the symbol is used
+
+- docs: introduce "curldown" for libcurl man page format
+
+ curldown is this new file format for libcurl man pages. It is markdown
+ inspired with differences:
+
+ - Each file has a set of leading headers with meta-data
+ - Supports a small subset of markdown
+ - Uses .md file extensions for editors/IDE/GitHub to treat them nicely
+ - Generates man pages very similar to the previous ones
+ - Generates man pages that still convert nicely to HTML on the website
+ - Detects and highlights mentions of curl symbols automatically (when
+ their man page section is specified)
+
+ tools:
+
+ - cd2nroff: converts from curldown to nroff man page
+ - nroff2cd: convert an (old) nroff man page to curldown
+ - cdall: convert many nroff pages to curldown versions
+ - cd2cd: verifies and updates a curldown to latest curldown
+
+ This setup generates .3 versions of all the curldown versions at build time.
+
+ CI:
+
+ Since the documentation is now technically markdown in the eyes of many
+ things, the CI runs many more tests and checks on this documentation,
+ including proselint, link checkers and tests that make sure we capitalize the
+ first letter after a period...
+
+ Closes #12730
+
+Viktor Szakats (22 Jan 2024)
+
+- libssh2: use `libssh2_session_callback_set2()` with v1.11.1
+
+ To avoid a local hack to pass function pointers and to avoid
+ deprecation warnings when building with libssh2 v1.11.1 or newer:
+ ```
+ lib/vssh/libssh2.c:3324:5: warning: 'libssh2_session_callback_set' is depreca
+ ted: since libssh2 1.11.1. Use libssh2_session_callback_set2() [-Wdeprecated-
+ declarations]
+ lib/vssh/libssh2.c:3326:5: warning: 'libssh2_session_callback_set' is depreca
+ ted: since libssh2 1.11.1. Use libssh2_session_callback_set2() [-Wdeprecated-
+ declarations]
+ ```
+ Ref: https://github.com/curl/curl-for-win/actions/runs/7609484879/job/2072082
+ 1100#step:3:4982
+
+ Ref: https://github.com/libssh2/libssh2/pull/1285
+ Ref: https://github.com/libssh2/libssh2/commit/c0f69548be902147ce014ffa40b8db
+ 3cf1d4b0b4
+ Reviewed-by: Daniel Stenberg
+ Closes #12754
+
+Daniel Stenberg (22 Jan 2024)
+
+- transfer: make the select_bits_paused condition check both directions
+
+ If there is activity in a direction that is not paused, return false.
+
+ Reported-by: Sergey Bronnikov
+ Bug: https://curl.se/mail/lib-2024-01/0049.html
+ Closes #12740
+
+Stefan Eissing (22 Jan 2024)
+
+- http3: initial support for OpenSSL 3.2 QUIC stack
+
+ - HTTP/3 for curl using OpenSSL's own QUIC stack together
+ with nghttp3
+ - configure with `--with-openssl-quic` to enable curl to
+ build this. This requires the nghttp3 library
+ - implementation with the following restrictions:
+ * macOS has to use an unconnected UDP socket due to an
+ issue in OpenSSL's datagram implementation
+ See https://github.com/openssl/openssl/issues/23251
+ This makes connections to non-reponsive servers hang.
+ * GET requests will send the indicator that they have
+ no body in a separate QUIC packet. This may result
+ in processing delays or Transfer-Encodings on proxied
+ requests
+ * uploads that encounter blocks will use 100% cpu as
+ detection of these flow control issue is not working
+ (we have not figured out to pry that from OpenSSL).
+
+ Closes #12734
+
+Viktor Szakats (22 Jan 2024)
+
+- cmake: fix `ENABLE_MANUAL` option
+
+ Fix the `ENABLE_MANUAL` option. Set it to default to `OFF`.
+
+ Before this patch `ENABLE_MANUAL=ON` was a no-op, even though it was the
+ option designed to enable building and using the built-in curl manual.
+ (`USE_MANUAL=ON` option worked for this instead, by accident).
+
+ Ref: https://github.com/curl/curl/pull/12730#issuecomment-1902572409
+ Closes #12749
+
+Mohammadreza Hendiani (19 Jan 2024)
+
+- TODO: update broken link to ratelimit-headers draft
+
+ Closes #12741
+
+Daniel Stenberg (19 Jan 2024)
+
+- cmake: when USE_MANUAL=YES, build the curl.1 man page
+
+ Fixes KNOWN_BUG 15.4
+
+ Closes #12742
+
+- cmdline-opts/write-out.d: remove spurious double quotes
+
+Stefan Eissing (19 Jan 2024)
+
+- rtsp: Convert assertion into debug log
+
+ Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65934
+
+ - write excess bytes to the client where the standard excess bytes
+ checks will report any wrongness and fail the transfer
+
+ Fixes #12738
+ Closes #12739
+
+Daniel Stenberg (19 Jan 2024)
+
+- headers: remove assert from Curl_headers_push
+
+ The fuzzer managed to reach the function without a terminating CR or LF
+ so let's handle it normally. While there, remove the goto.
+
+ Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65839
+
+ Closes #12721
+
+- curl_easy_getinfo.3: remove the wrong time value count
+
+ It said "six" time values but they are eight by now. Remove the mention
+ of the amount.
+
+ Closes #12727
+
+Viktor Szakats (18 Jan 2024)
+
+- mbedtls: fix `-Wnull-dereference` and `-Wredundant-decls`
+
+ - Silence warning in mbedTLS v3.5.1 public headers:
+ ```
+ ./mbedtls/_x64-linux-musl/usr/include/psa/crypto_extra.h:489:14: warning: r
+ edundant redeclaration of 'psa_set_key_domain_parameters' [-Wredundant-decls]
+ ./mbedtls/_x64-linux-musl/usr/include/psa/crypto_struct.h:354:14: note: pre
+ vious declaration of 'psa_set_key_domain_parameters' was here
+ ```
+ Ref: https://github.com/libssh2/libssh2/commit/ecec68a2c13a9c63fe8c2dc457ae
+ 785a513e157c
+ Ref: https://github.com/libssh2/libssh2/pull/1226
+
+ - Fix compiler warnings seen with gcc 9.2.0 + cmake unity:
+ ```
+ ./curl/lib/vtls/mbedtls.c: In function 'mbedtls_bio_cf_read':
+ ./curl/lib/vtls/mbedtls.c:189:11: warning: null pointer dereference [-Wnull
+ -dereference]
+ 189 | nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &res
+ ult);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~
+ ./curl/lib/vtls/mbedtls.c: In function 'mbedtls_bio_cf_write':
+ ./curl/lib/vtls/mbedtls.c:168:14: warning: null pointer dereference [-Wnull
+ -dereference]
+ 168 | nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &
+ result);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~~~~
+ ```
+
+ - delete stray `#else`.
+
+ Closes #12720
+
+Daniel Stenberg (17 Jan 2024)
+
+- docs: cleanup nroff format use
+
+ - remove use of .BI for code snippet
+ - stop using .br, just do a blank line
+ - remove use of .PP
+ - remove use for .sp
+ - remove backslash in .IP
+ - use .IP instead of .TP
+
+ Closes #12731
+
+Stefan Eissing (17 Jan 2024)
+
+- test2307: fix expected failure code after ws refactoring
+
+ Fixes #12722
+ Closes #12728
+
+Jay Satiro (17 Jan 2024)
+
+- cf-socket: show errno in tcpkeepalive error messages
+
+ - If the socket keepalive options (TCP_KEEPIDLE, etc) cannot be set
+ then show the errno in the verbose error messages.
+
+ Ref: https://github.com/curl/curl/discussions/12715#discussioncomment-8151652
+
+ Closes https://github.com/curl/curl/pull/12726
+
+- tool_getparam: stop supporting `@filename` style for --cookie
+
+ The `@filename` style was never documented for --cookie <data|filename>
+ but prior to this change curl would accept it anyway and always treat a
+ @ prefixed string as a filename.
+
+ That's a problem if the string also contains a = sign because then it is
+ documented to be interpreted as a cookie string and not a filename.
+
+ Example:
+
+ `--cookie @foo=bar`
+
+ Before: Interpreted as load cookies from filename foo=bar.
+
+ After: Interpreted as cookie `@foo=bar` (name `@foo` and value `bar`).
+
+ Other curl options with a data/filename option-value use the `@filename`
+ to distinguish filenames which is probably how this happened. The
+ --cookie option has never been documented that way.
+
+ Ref: https://curl.se/docs/manpage.html#-b
+
+ Closes https://github.com/curl/curl/pull/12645
+
+Stefan Eissing (16 Jan 2024)
+
+- websockets: refactor decode chain
+
+ - use client writer stack for decoding frames
+ - move websocket protocol handler to ws.c
+
+ Closes #12713
+
+- websockets: check for negative payload lengths
+
+ - in en- and decoding, check the websocket frame payload lengths for
+ negative values (from curl_off_t) and error the operation in that case
+ - add test 2307 to verify
+
+ Closes #12707
+
+Daniel Stenberg (16 Jan 2024)
+
+- docs: mention env vars not used by schannel
+
+ Ref: #12704
+
+ Co-authored-by: Jay Satiro <raysatiro@yahoo.com>
+
+ Closes #12711
+
+- tool_operate: make --remove-on-error only remove "real" files
+
+ Reported-by: Harry Sintonen
+ Assisted-by: Dan Fandrich
+
+ Closes #12710
+
+Jay Wu (16 Jan 2024)
+
+- url: don't set default CA paths for Secure Transport backend
+
+ As the default for this backend is the native CA store.
+
+ Closes #12704
+
+Lin Sun (16 Jan 2024)
+
+- asyn-ares: with modern c-ares, use its default timeout
+
+ Closes #12703
+
+Daniel Stenberg (15 Jan 2024)
+
+- tool_operate: stop setting the file comment on Amiga
+
+ - the URL is capped at 80 cols, which ruins it if longer
+ - it does not strip off URL credentials
+ - it is done unconditonally, not on --xattr
+ - we don't have Amiga in the CI which makes fixing it blindly fragile
+
+ Someone who builds and tests on Amiga can add it back correctly in a
+ future if there is a desire.
+
+ Reported-by: Harry Sintonen
+ Closes #12709
+
+Stefan Eissing (15 Jan 2024)
+
+- rtsp: deal with borked server responses
+
+ - enforce a response body length of 0, if the
+ response has no Content-lenght. This is according
+ to the RTSP spec.
+ - excess bytes in a response body are forwarded to
+ the client writers which will report and fail the
+ transfer
+
+ Follow-up to d7b6ce6
+ Fixes #12701
+ Closes #12706
+
+Daniel Stenberg (14 Jan 2024)
+
+- version: show only the libpsl version, not its dependencies
+
+ The libpsl version output otherwise also includes version number for its
+ dependencies, like IDN lib, but since libcurl does not use libpsl's IDN
+ functionality those components are not important.
+
+ Ref: https://github.com/curl/curl-for-win/issues/63
+ Closes #12700
+
+Brad Harder (14 Jan 2024)
+
+- curl.h: CURLOPT_DNS_SERVERS is only available with c-ares
+
+ Closes #12695
+
+Daniel Stenberg (14 Jan 2024)
+
+- cmdline-opts/gen.pl: error on initital blank line
+
+ After the "---" separator, there should be no blank line and this script
+ now errors out if one is detected.
+
+ Ref: #12696
+ Closes #12698
+
+- cf-h1-proxy: no CURLOPT_USERAGENT in CONNECT with hyper
+
+ Follow-up to 693cd1679361828a which was incomplete
+
+ Ref #12680
+ Closes #12697
+
+- curl_multi_fdset.3: remove mention of null pointer support
+
+ ... since this funtion has not supported null pointer fd_set arguments since
+ at least 2006. (That's when I stopped my git blame journey)
+
+ Fixes #12691
+ Reported-by: sfan5 on github
+ Closes #12692
+
+Mark Huang (14 Jan 2024)
+
+- docs/cmdline: remove unnecessary line breaks
+
+ Closes #12696
+
+Daniel Stenberg (14 Jan 2024)
+
+- transfer: remove warning: Value stored to 'blen' is never read
+
+ Detected by scan-build
+
+ Follow-up from 1cd2f0072f
+
+ Closes #12693
+
+Stefan Eissing (13 Jan 2024)
+
+- lib: replace readwrite with write_resp
+
+ This clarifies the handling of server responses by folding the code for
+ the complicated protocols into their protocol handlers. This concerns
+ mainly HTTP and its bastard sibling RTSP.
+
+ The terms "read" and "write" are often used without clear context if
+ they refer to the connect or the client/application side of a
+ transfer. This PR uses "read/write" for operations on the client side
+ and "send/receive" for the connection, e.g. server side. If this is
+ considered useful, we can revisit renaming of further methods in another
+ PR.
+
+ Curl's protocol handler `readwrite()` method been changed:
+
+ ```diff
+ - CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
+ - const char *buf, size_t blen,
+ - size_t *pconsumed, bool *readmore);
+ + CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t ble
+ n,
+ + bool is_eos, bool *done);
+ ```
+
+ The name was changed to clarify that this writes reponse data to the
+ client side. The parameter changes are:
+
+ * `conn` removed as it always operates on `data->conn`
+ * `pconsumed` removed as the method needs to handle all data on success
+ * `readmore` removed as no longer necessary
+ * `is_eos` as indicator that this is the last call for the transfer
+ response (end-of-stream).
+ * `done` TRUE on return iff the transfer response is to be treated as
+ finished
+
+ This change affects many files only because of updated comments in
+ handlers that provide no implementation. The real change is that the
+ HTTP protocol handlers now provide an implementation.
+
+ The HTTP protocol handlers `write_resp()` implementation will get passed
+ **all** raw data of a server response for the transfer. The HTTP/1.x
+ formatted status and headers, as well as the undecoded response
+ body. `Curl_http_write_resp_hds()` is used internally to parse the
+ response headers and pass them on. This method is public as the RTSP
+ protocol handler also uses it.
+
+ HTTP/1.1 "chunked" transport encoding is now part of the general
+ *content encoding* writer stack, just like other encodings. A new flag
+ `CLIENTWRITE_EOS` was added for the last client write. This allows
+ writers to verify that they are in a valid end state. The chunked
+ decoder will check if it indeed has seen the last chunk.
+
+ The general response handling in `transfer.c:466` happens in function
+ `readwrite_data()`. This mainly operates now like:
+
+ ```
+ static CURLcode readwrite_data(data, ...)
+ {
+ do {
+ Curl_xfer_recv_resp(data, buf)
+ ...
+ Curl_xfer_write_resp(data, buf)
+ ...
+ } while(interested);
+ ...
+ }
+ ```
+
+ All the response data handling is implemented in
+ `Curl_xfer_write_resp()`. It calls the protocol handler's `write_resp()`
+ implementation if available, or does the default behaviour.
+
+ All raw response data needs to pass through this function. Which also
+ means that anyone in possession of such data may call
+ `Curl_xfer_write_resp()`.
+
+ Closes #12480
+
+Daniel Stenberg (13 Jan 2024)
+
+- RELEASE-NOTES: synced
+
+- TODO: TFTP doesn't convert LF to CRLF for mode=netascii
+
+ Closes #12655
+ Closes #12690
+
+- gen: do italics/bold for a range of letters, not just single word
+
+ Previously it would match only on a sequence of non-space, which made it
+ miss to highlight for example "public suffix list".
+
+ Updated the recent cookie.d edit from 5da57193b732 to use bold instead
+ of italics.
+
+ Closes #12689
+
+- docs: describe and highlight super cookies
+
+ Reported-by: Yadhu Krishna M
+
+ Closes #12687
+
+- configure: when enabling QUIC, check that TLS supports QUIC
+
+ Most importantly perhaps is when using OpenSSL that the used
+ build/flavor has the QUIC API: the vanilla OpenSSL does not, only
+ BoringSSL, libressl, AWS-LC and quictls do.
+
+ Ref: https://github.com/curl/curl/commit/5d044ad9480a9f556f4b6a252d7533b1ba7f
+ e57e#r136780413
+
+ Closes #12683
+
+Stefan Eissing (11 Jan 2024)
+
+- vquic: extract TLS setup into own source
+
+ - separate ngtcp2 specific parts out
+ - provide callback during init to allow ngtcp2 to apply its defaults
+
+ Closes #12678
+
+Sergey Markelov (11 Jan 2024)
+
+- multi: remove total timer reset in file_do() while fetching file://
+
+ The total timer is properly reset in MSTATE_INIT. MSTATE_CONNECT starts
+ with resetting the timer that is a start point for further multi states.
+ If file://, MSTATE_DO calls file_do() that should not reset the total
+ timer. Otherwise, the total time is always less than the pre-transfer
+ and the start transfer times.
+
+ Closes #12682
+
+Daniel Stenberg (11 Jan 2024)
+
+- http_proxy: a blank CURLOPT_USERAGENT should not be used in CONNECT
+
+ Extended test 80 to verify this.
+
+ Reported-by: Stefan Eissing
+ Fixes #12680
+ Closes #12681
+
+- sectransp: do verify_cert without memdup for blobs
+
+ Since the information is then already stored in memory, this can avoid
+ an extra set of malloc + free calls.
+
+ Closes #12679
+
+- hsts: remove assert for zero length domain
+
+ A zero length domain can happen if the HSTS parser is given invalid
+ input data which is not unheard of and is done by the fuzzer.
+
+ Follow-up from cfe7902111ae547873
+
+ Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65661
+
+ Closes #12676
+
+- headers: make sure the trailing newline is not stored
+
+ extended test1940 to verify blank header fields too
+
+ Bug: https://curl.se/mail/lib-2024-01/0019.html
+ Reported-by: Dmitry Karpov
+ Closes #12675
+
+- curl_easy_header.3: tiny language fix
+
+ Closes #12672
+
+- examples/range.c: add
+
+ Closes #12671
+
+- examples/netrc.c: add
+
+ Closes #12671
+
+- examples/ipv6.c: new example showing IPv6-only internet transfer
+
+ Closes #12671
+
+- examples/address-scope.c: renamed from ipv6.c
+
+ It shows address scope use really
+
+ Closes #12671
+
+Stefan Eissing (9 Jan 2024)
+
+- multi: pollset adjust, init with FIRSTSOCKET during connect
+
+ - `conn->sockfd` is set by `Curl_setup_transfer()`, but that
+ is called *after* the connection has been established
+ - use `conn->sock[FIRSTSOCKET]` instead
+
+ Follow-up to a0f94800d507de
+ Closes #12664
+
+Daniel Stenberg (9 Jan 2024)
+
+- WEBSOCKET.md: remove dead link
+
+- CI: spellcheck/appveyor: invoke configure --without-libpsl
+
+ Follow-up to 2998874bb61ac6
+
+- cmdline/docs/*.d: switch to using ## instead of .IP
+
+ To make the editing easier. To write and to read.
+
+ Closes #12667
+
+- gen.pl: support ## for doing .IP in table-like lists
+
+ Warn on use of .RS/.IP/.RE
+
+ Closes #12667
+
+Jay Satiro (9 Jan 2024)
+
+- cookie.d: Document use of empty string to enable cookie engine
+
+ - Explain that --cookie "" can be used to enable the cookie engine
+ without reading any initial cookies.
+
+ As is documented in CURLOPT_COOKIEFILE.
+
+ Ref: https://curl.se/libcurl/c/CURLOPT_COOKIEFILE.html
+
+ Bug: https://github.com/curl/curl/issues/12643#issuecomment-1879844420
+ Reported-by: janko-js@users.noreply.github.com
+
+ Closes https://github.com/curl/curl/pull/12646
+
+Daniel Stenberg (9 Jan 2024)
+
+- setopt: use memdup0 when cloning COPYPOSTFIELDS
+
+ Closes #12651
+
+- telnet: use dynbuf instad of malloc for escape buffer
+
+ Previously, send_telnet_data() would malloc + free a buffer every time
+ for escaping IAC codes. Now, it reuses a dynbuf for this purpose.
+
+ Closes #12652
+
+- CI: install libpsl or configure --without-libpsl in builds
+
+ As a follow-up to the stricted libpsl check in configure
+
+- configure: make libpsl detection failure cause error
+
+ To force users to explictily disable it if they really don't want it
+ used and make it harder to accidentally miss it.
+
+ --without-libpsl is the option to use if PSL is not wanted.
+
+ Closes #12661
+
+- RELEASE-NOTES: synced
+
+- pop3: replace calloc + memcpy with memdup0
+
+ ... and make sure to return error on out of memory.
+
+ Closes #12650
+
+- lib: add debug log outputs for CURLE_BAD_FUNCTION_ARGUMENT
+
+ Closes #12658
+
+- mime: use memdup0 instead of malloc + memcpy
+
+ Closes #12649
+
+- tool_getparam: move the --rate logic into set_rate()
+
+- tool_getparam: switch to an enum for every option
+
+ To make the big switch much easier to read/understand and to make it
+ easier to add new options.
+
+- tool_getparam: build post data using dynbuf (more)
+
+- tool_getparam: replace malloc + copy by dynbuf for --data
+
+- tool_getparam: make data_urlencode avoid direct malloc
+
+ use aprintf() instead
+
+- tool_getparam: move the --url-query logic into url_query()
+
+ This function is not doing post at all so it was always weirdly placed.
+
+- tool_getparam: move the --data logic into set_data()
+
+- tool_getparam: unify the cmdline switch() into a single one
+
+ - easier to follow, easier to modify, easier to extend, possibly slightly
+ faster
+
+ - each case now has the long option as a comment
+
+- tool_getparam: bsearch cmdline options
+
+ - the option names are now alpha sorted and lookup is a lot faster
+
+ - use case sensitive matching. It was previously case insensitive, but that
+ was not documented nor tested.
+
+ - remove "partial match" feature. It was not documented, not tested and
+ was always fragile as existing use could break when we add a new
+ option
+
+ - lookup short options via a table
+
+ Closes #12631
+
+Gabe (8 Jan 2024)
+
+- COPYING: update copyright year
+
+ Closes #12654
+
+Stefan Eissing (8 Jan 2024)
+
+- url: init conn->sockfd and writesockfd to CURL_SOCKET_BAD
+
+ Also add more tracing to test 19
+
+ Follow-up to a0f9480
+
+ Fixes #12657
+ Closes #12659
+
+Daniel Stenberg (8 Jan 2024)
+
+- connect: remove margin from eyeballer alloc
+
+ Presumably leftovers from debugging
+
+ Closes #12647
+
+- ftp: only consider entry path if it has a length
+
+ Follow-up from 8edcfedc1a144f438bd1cdf814a0016cb
+
+ Bug: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65631
+
+ Avoids a NULL pointer deref.
+
+ Closes #12648
+
+Stefan Eissing (7 Jan 2024)
+
+- transfer: adjust_pollset improvements
+
+ - let `multi_getsock()` initialize the pollset in what the
+ transfer state requires in regards to SEND/RECV
+ - change connection filters `adjust_pollset()` implementation
+ to react on the presence of POLLIN/-OUT in the pollset and
+ no longer check CURL_WANT_SEND/CURL_WANT_RECV
+ - cf-socket will no longer add POLLIN on its own
+ - http2 and http/3 filters will only do adjustments if the
+ passed pollset wants to POLLIN/OUT for the transfer on
+ the socket. This is similar to the HTTP/2 proxy filter
+ and works in stacked filters.
+
+ Closes #12640
+
+Daniel Stenberg (6 Jan 2024)
+
+- ftp: use memdup0 to store the OS from a SYST 215 response
+
+ avoid malloc + direct buffer fiddle
+
+ Closes #12639
+
+- ftp: use dynbuf to store entrypath
+
+ avoid direct malloc
+
+ Closes #12638
+
+Lealem Amedie (6 Jan 2024)
+
+- wolfssl: load certificate *chain* for PEM client certs
+
+ Closes #12634
+
+Stefan Eissing (4 Jan 2024)
+
+- http: adjust_pollset fix
+
+ do not add a socket for POLLIN when the transfer does not want to send
+ (for example is paused).
+
+ Follow-up to 47f5b1a
+
+ Reported-by: bubbleguuum on github
+ Fixes #12632
+ Closes #12633
+
+Daniel Stenberg (3 Jan 2024)
+
+- tool: make parser reject blank arguments if not supported
+
+ Already in the getstr() function that clones the input argument.
+
+ Closes #12620
+
+dependabot[bot] (3 Jan 2024)
+
+- build(deps): bump github/codeql-action from 2 to 3
+
+ Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2
+ to 3.
+ - [Release notes](https://github.com/github/codeql-action/releases)
+ - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
+ - [Commits](https://github.com/github/codeql-action/compare/v2...v3)
+
+ ---
+ updated-dependencies:
+ - dependency-name: github/codeql-action
+ dependency-type: direct:production
+ update-type: version-update:semver-major
+ ...
+
+ Signed-off-by: dependabot[bot] <support@github.com>
+
+ Closes #12625
+
+- build(deps): bump actions/checkout from 3 to 4
+
+ Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
+ - [Release notes](https://github.com/actions/checkout/releases)
+ - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
+ - [Commits](https://github.com/actions/checkout/compare/v3...v4)
+
+ ---
+ updated-dependencies:
+ - dependency-name: actions/checkout
+ dependency-type: direct:production
+ update-type: version-update:semver-major
+ ...
+
+ Signed-off-by: dependabot[bot] <support@github.com>
+
+ Closes #12624
+
+- build(deps): bump actions/upload-artifact from 3 to 4
+
+ Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) f
+ rom 3 to 4.
+ - [Release notes](https://github.com/actions/upload-artifact/releases)
+ - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)
+
+ ---
+ updated-dependencies:
+ - dependency-name: actions/upload-artifact
+ dependency-type: direct:production
+ update-type: version-update:semver-major
+ ...
+
+ Signed-off-by: dependabot[bot] <support@github.com>
+
+ Closes #12627
+
+- build(deps): bump actions/download-artifact from 3 to 4
+
+ Bumps [actions/download-artifact](https://github.com/actions/download-artifac
+ t) from 3 to 4.
+ - [Release notes](https://github.com/actions/download-artifact/releases)
+ - [Commits](https://github.com/actions/download-artifact/compare/v3...v4)
+
+ ---
+ updated-dependencies:
+ - dependency-name: actions/download-artifact
+ dependency-type: direct:production
+ update-type: version-update:semver-major
+ ...
+
+ Signed-off-by: dependabot[bot] <support@github.com>
+
+ Closes #12626
+
+Stefan Eissing (3 Jan 2024)
+
+- http3/quiche: fix result code on a stream reset
+
+ - fixes pytest failures in test 07_22
+ - aligns CURLcode values on stream reset with ngtcp2
+
+ Closes #12629
+
+Daniel Stenberg (2 Jan 2024)
+
+- setopt: clear mimepost when formp is freed
+
+ A precaution to avoid a possibly dangling pointer left behind.
+
+ Reported-by: Thomas Ferguson
+ Fixes #12608
+ Closes #12621
+
+Andy Alt (2 Jan 2024)
+
+- CI: Add dependabot.yml
+
+ This will cause dependabot to open a PR when various actions are
+ updated, provided that the action maintainer has issued a release.
+
+ Closes #12623
+
+Gisle Vanem (2 Jan 2024)
+
+- content_encoding: change return code to typedef'ed enum
+
+ ... to work around a clang ubsan warning.
+
+ Fixes #12618
+ Closes #12622
+
+Daniel Stenberg (2 Jan 2024)
+
+- tool: prepend output_dir in header callback
+
+ When Content-Disposition parsing is used and an output dir is prepended,
+ make sure to store that new file name correctly so that it can be used
+ for setting the file timestamp when --remote-time is used.
+
+ Extended test 3012 to verify.
+
+ Co-Authored-by: Jay Satiro
+ Reported-by: hgdagon on github
+ Fixes #12614
+ Closes #12617
+
+- test1254: fix typo in name plus shorten it
+
+- RELEASE-NOTES: synced
+
+Viktor Szakats (2 Jan 2024)
+
+- schannel: fix `-Warith-conversion` gcc 13 warning
+
+ ```
+ lib/vtls/schannel.c:1201:22: warning: conversion to 'unsigned int' from 'int'
+ may change the sign of the result [-Warith-conversion]
+ 1201 | *extension_len = *list_len +
+ | ^
+ ```
+
+ Closes #12616
+
+- asyn-thread: silence `-Wcast-align` warning for Windows
+
+ Seen with llvm/clang 17:
+ ```
+ lib/asyn-thread.c:310:5: warning: cast from 'PCHAR' (aka 'char *') to 'struct
+ thread_sync_data *' increases required alignment from 1 to 8 [-Wcast-align]
+ 310 | CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlap
+ ped);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~
+ .../llvm-mingw/aarch64-w64-mingw32/include/winnt.h:717:48: note: expanded fro
+ m macro 'CONTAINING_RECORD'
+ 717 | #define CONTAINING_RECORD(address,type,field) ((type *)((PCHAR)(addre
+ ss) - (ULONG_PTR)(&((type *)0)->field)))
+ | ^~~~~~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ```
+
+ Follow-up to a6bbc87f9e9ffb46a1801dfb983e7534825ed56b #12482
+
+ Ref: https://github.com/curl/curl/pull/12482#issuecomment-1873017261
+ Closes #12615
+
+Daniel Stenberg (2 Jan 2024)
+
+- tool_listhelp: regenerate after recent .d updates
+
+ Makes it survive test 1478
+
+ Closes #12612
+
+- test1478: verify src/tool_listhelp.c
+
+ Verify that the source file on disk is identical to the output of gen.pl
+ listhelp, as otherwise they are out of sync and need attention.
+
+ Closes #12612
+
+- testutil: make runtests support %include
+
+ Using this instruction, a test case can include the contents of a file
+ into the test during the preprocessing.
+
+ Closes #12612
+
+- runtests: for mode="text" on <stdout>, fix newlines on both parts
+
+ Closes #12612
+
+Jay Satiro (2 Jan 2024)
+
+- quiche: return CURLE_HTTP3 on send to invalid stream
+
+ Prior to this change if a send failed on a stream in an invalid state
+ (according to quiche) and not marked as closed (according to libcurl)
+ then the send function would return CURLE_SEND_ERROR.
+
+ We already have similar code for ngtcp2 to return CURLE_HTTP3 in this
+ case.
+
+ Caught by test test_07_upload.py: test_07_22_upload_parallel_fail.
+
+ Fixes https://github.com/curl/curl/issues/12590
+ Closes https://github.com/curl/curl/pull/12597
+
+Daniel Stenberg (1 Jan 2024)
+
+- cmdline-opts: update availability for the *-ca-native options
+
+ Closes #12613
+
+Patrick Monnerat (31 Dec 2023)
+
+- openldap: fix STARTTLS
+
+ It was not working anymore since introduction of connection filters.
+
+ Also do not attempt to recover from a failing TLS negotiation with
+ CURLUSESSL_TRY.
+
+ Closes #12610
+
+Daniel Stenberg (31 Dec 2023)
+
+- haproxy-clientip.d: document the arg
+
+ The arg keyword was missing and therefore not present in the man page.
+
+ Closes #12611
+
+annalee (29 Dec 2023)
+
+- configure: fix no default int compile error in ipv6 detection
+
+ Closes #12607
+
+Dan Fandrich (28 Dec 2023)
+
+- CI: Fix use of any-glob-to-all-files in the labeler
+
+ Despite its name, this atom acts like one-glob-to-all-files and a
+ different syntax with braces must be used to get
+ any-glob-to-all-files semantics. Unfortunately, this makes the file
+ completely unreadable.
+
+ Ref: https://github.com/actions/labeler/issues/731
+
+Daniel Stenberg (29 Dec 2023)
+
+- CURLOPT_AUTOREFERER.3: mention CURLINFO_REFERER
+
+- CURLINFO_REFERER.3: clarify that it is the *request* header
+
+ That libcurl itself sent in the most recent request
+
+ Closes #12605
+
+Jay Satiro (28 Dec 2023)
+
+- system_win32: fix a function pointer assignment warning
+
+ - Use CURLX_FUNCTION_CAST to suppress a function pointer assignment
+ warning.
+
+ a6bbc87f added lookups of some Windows API functions and then cast them
+ like `*(FARPROC*)&Curl_funcname = address`. Some versions of gcc warn
+ about that as breaking strict-aliasing rules so this PR changes those
+ assignments to use CURLX_FUNCTION_CAST.
+
+ Bug: https://github.com/curl/curl/pull/12581#issuecomment-1869804317
+ Reported-by: Marcel Raad
+
+ Closes https://github.com/curl/curl/pull/12602
+
+- verify-examples.pl: fail verification on unescaped backslash
+
+ - Check that all backslashes in EXAMPLE are properly escaped.
+
+ eg manpage must always use `\\n` never `\n`.
+
+ This is because the manpage requires we always double blackslash to show
+ a single backslash. Prior to this change an erroneous single backslash
+ would pass through and compile even though it would not show correctly
+ in the manpage.
+
+ Co-authored-by: Daniel Stenberg
+
+ Ref: https://github.com/curl/curl/pull/12588
+
+ Closes https://github.com/curl/curl/pull/12589
+
+- vtls: fix missing multissl version info
+
+ - Fix erroneous buffer copy logic from ff74cef5.
+
+ Prior to this change the MultiSSL version info returned to the user
+ was empty.
+
+ Closes https://github.com/curl/curl/pull/12599
+
+Daniel Stenberg (27 Dec 2023)
+
+- KNOWN_BUGS: [RTSP] Some methods do not support response bodies
+
+ Closes #12414
+
+Patrick Monnerat (27 Dec 2023)
+
+- openldap: fix an LDAP crash
+
+ Reported-by: Ozan Cansel
+ Fixes #12593
+ Closes #12600
+
+Daniel Stenberg (27 Dec 2023)
+
+- getinfo: CURLINFO_QUEUE_TIME_T
+
+ Returns the time, in microseconds, during which this transfer was held
+ in a waiting queue before it started "for real". A transfer might be put
+ in a queue if after getting started, it cannot create a new connection
+ etc due to set conditions and limits imposed by the application.
+
+ Ref: #12293
+ Closes #12368
+
+- RELEASE-NOTES: synced
+
+Jay Satiro (26 Dec 2023)
+
+- examples/sendrecv: fix comment line length
+
+ Caught by checksrc.
+
+Haydar Alaidrus (23 Dec 2023)
+
+- CURLOPT_POSTFIELDS.3: fix incorrect C string escape in example
+
+ - Escape inner quotes with two backslashes.
+
+ Two backslashes escapes the backslash for the man page and will show as
+ a single backslash.
+
+ eg: "{\\"name\\": \\"daniel\\"}" shows as "{\"name\": \"daniel\"}".
+
+ Closes https://github.com/curl/curl/pull/12588
+
+Viktor Szakats (23 Dec 2023)
+
+- appveyor: tidy-ups
+
+ - replace two remaining backslashes with forward slashes.
+ - tidy up the way we form and pass `TFLAGS`.
+
+ Follow-up to 2d4d0c1fd32f5cc3f946c407c8eccd5477b287df #12572
+
+ Closes #12582
+
+Stefan Eissing (22 Dec 2023)
+
+- transfer: fix upload rate limiting, add test cases
+
+ - add test cases for rate limiting uploads for all
+ http versions
+ - fix transfer loop handling of limits. Signal a re-receive
+ attempt only on exhausting maxloops without an EAGAIN
+ - fix `data->state.selectbits` forcing re-receive to also
+ set re-sending when transfer is doing this.
+
+ Reported-by: Karthikdasari0423 on github
+ Fixes #12559
+ Closes #12586
+
+Daniel Stenberg (22 Dec 2023)
+
+- mbedtls: free the entropy when threaded
+
+ The entropy_free was never done for threaded builds, causing a small
+ (fixed) memory leak.
+
+ Reported-by: RevaliQaQ on github
+ Fixes #12584
+ Closes #12585
+
+Stefan Eissing (22 Dec 2023)
+
+- http2: improved on_stream_close/data_done handling
+
+ - there seems to be a code path that cleans up easy handles without
+ triggering DONE or DETACH events to the connection filters. This
+ would explain wh nghttp2 still holds stream user data
+ - add GOOD check to easy handle used in on_close_callback to
+ prevent crashes, ASSERTs in debug builds.
+ - NULL the stream user data early before submitting RST
+ - add checks in on_stream_close() to identify UNGOOD easy handles
+
+ Reported-by: Hans-Christian Egtvedt
+ Fixes #10936
+ Closes #12562
+
+Daniel Stenberg (22 Dec 2023)
+
+- mprintf: overhaul and bugfixes
+
+ In a test case using lots of snprintf() calls using many commonly used
+ %-codes per call, this version is around 30% faster than previous
+ version.
+
+ It also fixes the #12561 bug which made it not behave correctly when
+ given unknown %-sequences. Fixing that flaw required a different take on
+ the problem, which resulted in the new two-arrays model.
+
+ lib557: extended - Verify the #12561 fix and test more printf features
+
+ unit1398: fix test: It used a <num>$ only for one argument, which is not
+ supported.
+
+ Fixes #12561
+ Closes #12563
+
+Viktor Szakats (21 Dec 2023)
+
+- appveyor: replace PowerShell with bash + parallel autotools
+
+ PowerShell works (after a steep development curve), but one property of
+ it stuck and kept causing unresolvable usability issues: With
+ `$ErrorActionPreference=Stop`, it does abort on failures, but shows only
+ the first line of the error message. In `Continue` mode, it shows the
+ full error message, but doesn't stop on all errors. Another issue is
+ PowerShell considering any stderr output as if the command failed (this
+ has been improved in 7.2 (2021-Nov), but fixed versions aren't running
+ in CI and will not be for a long time in all test images.)
+
+ Thus, we're going with bash.
+
+ Also:
+ - use `-j2` with autotools tests, making them finish 5-15 minutes per
+ job faster.
+ - omit `POSIX_PATH_PREFIX`.
+ - use `WINDIR`.
+ - prefer forward slashes.
+
+ Follow-up to: 75078a415d9c769419aed4153d3d525a8eba95af #11999
+ Ref: #12444
+
+ Fixes #12560
+ Closes #12572
+
+Pavel Pavlov (21 Dec 2023)
+
+- asyn-thread: use GetAddrInfoExW on >= Windows 8
+
+ For doing async DNS resolution instead of starting a thread for each
+ request.
+
+ Fixes #12481
+ Closes #12482
+
+Daniel Stenberg (21 Dec 2023)
+
+- strerror: repair get_winsock_error()
+
+ It would try to read longer than the provided string and crash.
+
+ Follow-up to ff74cef5d4a0cf60106517a1c7384
+ Reported-by: calvin2021y on github
+ Fixes #12578
+ Closes #12579
+
+- CURLOPT_SSH_*_KEYFILE: clarify
+
+ Closes #12554
+
+ivanfywang (21 Dec 2023)
+
+- ngtcp2: put h3 at the front of alpn
+
+ Closes #12576
+
+Daniel Stenberg (21 Dec 2023)
+
+- test460: verify a command line using --expand with no argument
+
+ This verifies the fix for #12565
+
+- tool_getparam: do not try to expand without an argument
+
+ This would lead to a segfault.
+
+ Fixes #12565
+ Reported-by: Geeknik Labs
+ Closes #12575
+
+- RELEASE-NOTES: synced
+
+ Bumped version to 8.6.0 because of changes
+
+- Makefile.am: fix the MSVC project generation
+
+ It made the vcxproj files not get included in dist tarballs.
+
+ Regression since 74423b5df4c8117891eb89 (8.5.0)
+
+ Reported-by: iAroc on github
+ Fixes #12564
+ Closes #12567
+
+zengwei2000 (21 Dec 2023)
+
+- altsvc: free 'as' when returning error
+
+ Closes #12570
+
+ Signed-off-by: zengwei <zengwei1@uniontech.com>
+
+Viktor Szakats (20 Dec 2023)
+
+- build: fix `-Wconversion`/`-Wsign-conversion` warnings
+
+ Fix remaining warnings in examples and tests which are not suppressed
+ by the pragma in `lib/curl_setup.h`.
+
+ Silence a toolchain issue causing warnings in `FD_SET()` calls with
+ older Cygwin/MSYS2 builds. Likely fixed on 2020-08-03 by:
+ https://cygwin.com/git/?p=newlib-cygwin.git;a=commitdiff;h=5717262b8ecfed0f7f
+ ab63e2c09c78991e36f9dd
+
+ Follow-up to 2dbe75bd7f3c36837aa06fd87a442bdf3fb7faef #12492
+
+ Closes #12557
+
+- build: fix some `-Wsign-conversion`/`-Warith-conversion` warnings
+
+ - enable `-Wsign-conversion` warnings, but also setting them to not
+ raise errors.
+ - fix `-Warith-conversion` warnings seen in CI.
+ These are triggered by `-Wsign-converion` and causing errors unless
+ explicitly silenced. It makes more sense to fix them, there just a few
+ of them.
+ - fix some `-Wsign-conversion` warnings.
+ - hide `-Wsign-conversion` warnings with a `#pragma`.
+ - add macro `CURL_WARN_SIGN_CONVERSION` to unhide them on a per-build
+ basis.
+ - update a CI job to unhide them with the above macro:
+ https://github.com/curl/curl/actions/workflows/linux.yml -> OpenSSL -O3
+
+ Closes #12492
+
+- cmake: tidy-up `OtherTests.cmake`
+
+ - make more obvious which detection uses which prep steps.
+ - merge and streamline conditions.
+ - these should not alter detection results.
+
+ Also align log output messages from
+ `Macros.cmake` / `curl_internal_test` with rest of the build.
+
+ Closes #12551
+
+- appveyor: switch to out-of-tree builds
+
+ With cmake and autotools.
+
+ Closes #12550
+
+Daniel Stenberg (19 Dec 2023)
+
+- DEPRECATE.md: mention that NTLM_WB no longer works
+
+ Ref: #12479
+ Closes #12553
+
+- CURLOPT_SERVER_RESPONSE_TIMEOUT_MS: add
+
+ Proposed-by: Yifei Kong
+ Ref: https://curl.se/mail/lib-2023-11/0023.html
+ Closes #12369
+
+Viktor Szakats (18 Dec 2023)
+
+- build: more `-Wformat` fixes
+
+ - memdebug: update to not trigger `-Wformat-nonliteral` warnings.
+ - imap: mark `imap_sendf()` with `CURL_PRINTF()`.
+ - tool_msgs: mark static function with `CURL_PRINTF()`.
+
+ Follow-up to 3829759bd042c03225ae862062560f568ba1a231 #12489
+
+ Closes #12540
+
+- windows: delete redundant headers
+
+ `winsock2.h` pulls in `windows.h`. `ws2tcpip.h` pulls in `winsock2.h`.
+ `winsock2.h` and `ws2tcpip.h` are also pulled by `curl/curl.h`.
+
+ Keep only those headers that are not already included, or the code under
+ it uses something from that specific header.
+
+ Closes #12539
+
+- cmake: prefill/cache `HAVE_STRUCT_SOCKADDR_STORAGE`
+
+ Also add missing include to `OtherTests.cmake`. It didn't cause an issue
+ because the parent already included this earlier by chance.
+
+ Closes #12537
+
+Daniel Stenberg (18 Dec 2023)
+
+- runner.pm: fix perl warning when running tests
+
+ Use of uninitialized value $runner::gdbthis in numeric eq (==) at runner.
+ pm
+
+ Follow-up from 3dcf301752a09d9
+
+ Closes #12549
+
+- runtests: support -gl. Like -g but for lldb.
+
+ Follow-up to 63b5748
+
+ Invokes the test case via lldb instead of gdb. Since using gdb is such a
+ pain on mac, using lldb is sometimes less quirky.
+
+ Closes #12547
+
+- curl.h: add CURLE_TOO_LARGE
+
+ A new error code to be used when an internal field grows too large, like
+ when a dynbuf reaches its maximum. Previously it would return
+ CURLE_OUT_OF_MEMORY for this, which is highly misleading.
+
+ Ref: #12268
+ Closes #12269
+
+- CI/circleci: disable MQTT in the HTTP-only build
+
+ And remove the use of configure options that don't actually exist
+
+ Closes #12546
+
+Yedaya Katsman (18 Dec 2023)
+
+- tests: respect $TMPDIR when creating unix domain sockets
+
+ When running on termux, where $TMPDIR isn't /tmp, running the tests
+ failed, since the server config tried creating sockets in /tmp, without
+ checking the temp dir config. Use the TMPDIR variable that makes it find
+ the correct directory everywhere [0]
+
+ [0] https://perldoc.perl.org/File::Temp#tempfile
+
+ Closes #12545
+
+Viktor Szakats (17 Dec 2023)
+
+- ssh: fix namespace of two local macros
+
+ Avoid using the libssh and libssh2 macro namespaces by prefixing
+ these local macro names with `CURL_`.
+
+ Follow-up to 413a0fedd02c8c6df1d294534b8c6e306fcca7a2 #12346
+
+ Reviewed-by: Daniel Stenberg
+ Closes #12544
+
+- cmake: whitespace tidy-up in `OtherTests.cmake`
+
+ Closes #12538
+
+Mark Sinkovics (16 Dec 2023)
+
+- cmake: fix generation for system name iOS
+
+ This PR fixes a problem that happens during CMake configuration when
+ the `CMAKE_SYSTEM_NAME` set to `iOS` and not `Darwin`. This value is
+ available (as far as I remember) version 3.14. The final solution
+ (thanks to @vszakats) is to use `APPLE` which contains all the Apple
+ platforms https://cmake.org/cmake/help/latest/variable/APPLE.html.
+
+ This issue was found when during vcpkg installation. Running command
+ `vcpkg install curl:arm64-ios` and `vcpkg install curl:x64-ios` failed
+ with message:
+ ```
+ CMake Error: try_run() invoked in cross-compiling mode, please set the follow
+ ing cache variables appropriately:
+ HAVE_H_ERRNO_ASSIGNABLE_EXITCODE (advanced)
+ ```
+ After this fix, I was able to compile the compile the binary without
+ any issue.
+
+ In addition to that fix, this PR also contains an simplification to
+ check if the platform is not APPLE.
+
+ Co-authored-by: Viktor Szakats
+ Closes #12515
+
+Daniel Stenberg (16 Dec 2023)
+
+- RELEASE-NOTES: synced
+
+Baruch Siach (16 Dec 2023)
+
+- gnutls: fix build with --disable-verbose
+
+ infof() parameters must be defined event with --disable-verbose since
+ commit dac293cfb702 ("lib: apache style infof and trace
+ macros/functions").
+
+ Move also 'ptr' definition under !CURL_DISABLE_VERBOSE_STRINGS.
+
+ Fixes the following build failure:
+
+ In file included from ../lib/sendf.h:29,
+ from vtls/gtls.c:44:
+ vtls/gtls.c: In function 'Curl_gtls_verifyserver':
+ vtls/gtls.c:841:34: error: 'version' undeclared (first use in this function);
+ did you mean 'session'?
+ 841 | gnutls_protocol_get_name(version), ptr);
+ | ^~~~~~~
+
+ Closes #12505
+
+Viktor Szakats (16 Dec 2023)
+
+- build: delete unused `HAVE_{GSSHEIMDAL,GSSMIT,HEIMDAL}`
+
+ Stop setting `HAVE_GSSHEIMDAL`, `HAVE_GSSMIT` and `HAVE_HEIMDAL`.
+ There was no place in the build system or source code that used them.
+
+ Reviewed-by: Daniel Stenberg
+ Closes #12506
+
+- build: remove redundant `CURL_PULL_*` settings
+
+ These macros were not propagated to the source code from CMake.
+
+ autotools set only one of them (`CURL_PULL_SYS_POLL_H`), initially to
+ address an AIX issue [1]. This later broke when introducing `system.h`
+ [2] without the logic it enabled. A subsequent fix [3] re-added the
+ logic, and also enabled it for AIX before its use, directly in
+ `system.h`.
+
+ [1] 2012-11-23: 665adcd4b7bcdb7deb638cdc499fbe71f8d777f2
+ [2] 2017-03-29: 9506d01ee50d5908138ebad0fd9fbd39b66bd64d #1373
+ [3] 2017-08-25: 8a84fcc4b59e8b78d2acc6febf44a43d6bc81b59 #1828 #1833
+
+ Reviewed-by: Daniel Stenberg
+ Closes #12502
+
+- system.h: sync mingw `CURL_TYPEOF_CURL_SOCKLEN_T` with other compilers
+
+ Align mingw with the other Windows compilers and use the `int` type for
+ `CURL_TYPEOF_CURL_SOCKLEN_T` (and thus for `curl_socklent_t`). This
+ makes it unnecessary to make a mingw-specific trick and pull all Windows
+ headers early just for this type definition. This type is specific to
+ Windows, not to the compiler. mingw-w64's Windows header maps it to
+ `int` too.
+
+ With this we also delete all remaining uses of `CURL_PULL_WS2TCPIP_H`.
+
+ [ The official solution is to use `socklen_t` for all Windows compilers.
+ In this case we may want to update `curl/curl.h` to pull in Windows
+ headers before `system.h`. ]
+
+ Reviewed-by: Daniel Stenberg
+ Reviewed-by: Jay Satiro
+ Closes #12501
+
+- windows: simplify detecting and using system headers
+
+ - autotools, cmake: assume that if we detect Windows, `windows.h`,
+ `winsock2.h` and `ws2tcpip.h` do exist.
+ - lib: fix 3 outlier `#if` conditions to use `USE_WINSOCK` instead of
+ looking for `winsock2.h`.
+ - autotools: merge 3 Windows check methods into one.
+ - move Watt-32 and lwIP socket support to `setup-win32.h` from
+ `config-win32.h`. It opens up using these with all build tools. Also
+ merge logic with Windows Sockets.
+ - fix to assume Windows sockets with the mingw32ce toolchain.
+ Follow-up to: 2748c64d605b19fb419ae56810ad8da36487a2d4
+ - cmake: delete unused variable `signature_call_conv` since
+ eb33ccd5332435fa50f1758e5debb869c6942b7f.
+ - autotools: simplify `CURL_CHECK_WIN32_LARGEFILE` detection.
+ - examples/externalsocket: fix header order.
+ - cmake/OtherTests.cmake: delete Windows-specific `_source_epilogue`
+ that wasn't used anymore.
+ - cmake/OtherTests.cmake: set `WIN32_LEAN_AND_MEAN` for test
+ `SIZEOF_STRUCT_SOCKADDR_STORAGE`.
+
+ After this patch curl universally uses `_WIN32` to guard
+ Windows-specific logic. It guards Windows Sockets-specific logic with
+ `USE_WINSOCK` (this might need further work).
+
+ Reviewed-by: Jay Satiro
+ Closes #12495
+
+- build: enable missing OpenSSF-recommended warnings, with fixes
+
+ https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening
+ -Guide-for-C-and-C++.html
+ as of 2023-11-29 [1].
+
+ Enable new recommended warnings (except `-Wsign-conversion`):
+
+ - enable `-Wformat=2` for clang (in both cmake and autotools).
+ - add `CURL_PRINTF()` internal attribute and mark functions accepting
+ printf arguments with it. This is a copy of existing
+ `CURL_TEMP_PRINTF()` but using `__printf__` to make it compatible
+ with redefinting the `printf` symbol:
+ https://gcc.gnu.org/onlinedocs/gcc-3.0.4/gcc_5.html#SEC94
+ - fix `CURL_PRINTF()` and existing `CURL_TEMP_PRINTF()` for
+ mingw-w64 and enable it on this platform.
+ - enable `-Wimplicit-fallthrough`.
+ - enable `-Wtrampolines`.
+ - add `-Wsign-conversion` commented with a FIXME.
+ - cmake: enable `-pedantic-errors` the way we do it with autotools.
+ Follow-up to d5c0351055d5709da8f3e16c91348092fdb481aa #2747
+ - lib/curl_trc.h: use `CURL_FORMAT()`, this also fixes it to enable format
+ checks. Previously it was always disabled due to the internal `printf`
+ macro.
+
+ Fix them:
+
+ - fix bug where an `set_ipv6_v6only()` call was missed in builds with
+ `--disable-verbose` / `CURL_DISABLE_VERBOSE_STRINGS=ON`.
+ - add internal `FALLTHROUGH()` macro.
+ - replace obsolete fall-through comments with `FALLTHROUGH()`.
+ - fix fallthrough markups: Delete redundant ones (showing up as
+ warnings in most cases). Add missing ones. Fix indentation.
+ - silence `-Wformat-nonliteral` warnings with llvm/clang.
+ - fix one `-Wformat-nonliteral` warning.
+ - fix new `-Wformat` and `-Wformat-security` warnings.
+ - fix `CURL_FORMAT_SOCKET_T` value for mingw-w64. Also move its
+ definition to `lib/curl_setup.h` allowing use in `tests/server`.
+ - lib: fix two wrongly passed string arguments in log outputs.
+ Co-authored-by: Jay Satiro
+ - fix new `-Wformat` warnings on mingw-w64.
+
+ [1] https://github.com/ossf/wg-best-practices-os-developers/blob/56c0fde3895b
+ fc55c8a973ef49a2572c507b2ae1/docs/Compiler-Hardening-Guides/Compiler-Options-
+ Hardening-Guide-for-C-and-C%2B%2B.md
+
+ Closes #12489
+
+- Makefile.mk: drop Windows support
+
+ And DLL-support with it. This leaves `Makefile.mk` for MS-DOS and Amiga.
+
+ We recommend CMake instead. With unity mode it's much faster, and about
+ the same without.
+
+ Ref: https://github.com/curl/curl/pull/12221#issuecomment-1783761806
+ Reviewed-by: Daniel Stenberg
+ Closes #12224
+
+Daniel Stenberg (16 Dec 2023)
+
+- cmdline-docs: use .IP consistently
+
+ Remove use of .TP and some .B. The idea is to reduce nroff syntax as
+ much as possible and to use it consistently. Ultimately, we should be
+ able to introduce our own easier-to-use-and-read syntax/formatting and
+ convert on generation time.
+
+ Closes #12535
+
+Tatsuhiko Miyagawa (16 Dec 2023)
+
+- http: fix off-by-one error in request method length check
+
+ It should allow one more byte.
+
+ Closes #12534
+
+Daniel Stenberg (15 Dec 2023)
+
+- curl: show ipfs and ipns as supported "protocols"
+
+ They are accepted schemes in URLs passed to curl (the tool, not the
+ library).
+
+ Also makes curl-config show the same list.
+
+ Co-Authored-by: Jay Satiro
+ Reported-by: Chara White
+ Bug: https://curl.se/mail/archive-2023-12/0026.html
+ Closes #12508
+
+- Revert "urldata: move async resolver state from easy handle to connectdata"
+
+ This reverts commit 56a4db2e4e2bcb9a0dcb75b83560a78ef231fcc8 (#12198)
+
+ We want the c-ares channel to be held in the easy handle, not per
+ connection - for performance.
+
+ Closes #12524
+
+Viktor Szakats (15 Dec 2023)
+
+- openssl: re-match LibreSSL deinit with init
+
+ Earlier we switched to use modern initialization with LibreSSL v2.7.0
+ and up, but did not touch deinitialization [1]. Fix it in this patch.
+
+ Regression from bec0c5bbf34369920598678161d2df8bea0e243b #11611
+
+ [1] https://github.com/curl/curl/pull/11611#issuecomment-1668654014
+
+ Reported-by: Mike Hommey
+ Reviewed-by: Daniel Stenberg
+ Fixes #12525
+ Closes #12526
+
+Daniel Stenberg (14 Dec 2023)
+
+- libssh: supress warnings without version check
+
+ Define unconditionally.
+
+ Follow-up from d21bd2190c46ad7fa
+
+ Closes #12523
+
+- hostip: return error immediately when Curl_ip2addr() fails
+
+ Closes #12522
+
+Theo (14 Dec 2023)
+
+- libssh: improve the deprecation warning dismissal
+
+ Previous code was compiler dependant, and dismissed all deprecation warnings
+ indiscriminately.
+
+ libssh provides a way to disable the deprecation warnings for libssh only, an
+ d
+ naturally this is the preferred way.
+
+ This commit uses that, to prevent the erroneous hiding of potential, unrelate
+ d
+ deprecation warnings.
+
+ Fixes #12519
+ Closes #12520
+
+Daniel Stenberg (14 Dec 2023)
+
+- test1474: removed
+
+ The test was already somewhat flaky and disabled on several platforms,
+ and after 1da640abb688 even more unstable.
+
+- readwrite_data: loop less
+
+ This function is made to loop in order to drain incoming data
+ faster. Completely removing the loop has a measerably negative impact on
+ transfer speeds.
+
+ Downsides with the looping include
+
+ - it might call the progress callback much more seldom. Especially if
+ the write callback is slow.
+
+ - rate limiting becomes less exact
+
+ - a single transfer might "starve out" other parallel transfers
+
+ - QUIC timers for other connections can't be maintained correctly
+
+ The long term fix should be to remove the loop and optimize coming back
+ to avoid the transfer speed penalty.
+
+ This fix lower the max loop count to reduce the starvation problem, and
+ avoids the loop completely for when rate-limiting is in progress.
+
+ Ref: #12488
+ Ref: https://curl.se/mail/lib-2023-12/0012.html
+ Closes #12504
+
+Stefan Eissing (14 Dec 2023)
+
+- lib: eliminate `conn->cselect_bits`
+
+ - use `data->state.dselect_bits` everywhere instead
+ - remove `bool *comeback` parameter as non-zero
+ `data->state.dselect_bits` will indicate that IO is
+ incomplete.
+
+ Closes #12512
+
+- connect: refactor `Curl_timeleft()`
+
+ - less local vars, "better" readability
+ - added documentation
+
+ Closes #12518
+
+Dmitry Karpov (14 Dec 2023)
+
+- cookie: avoid fopen with empty file name
+
+ Closes #12514
+
+Viktor Szakats (13 Dec 2023)
+
+- tests/server: delete workaround for old-mingw
+
+ mingw-w64 1.0 comes with w32api v3.12, thus doesn't need this.
+
+ Follow-up to 38029101e2d78ba125732b3bab6ec267b80a0e72 #11625
+
+ Reviewed-by: Jay Satiro
+ Closes #12510
+
+- cmake: delete obsolete TODOs more [ci skip]
+
+ - manual completed: 898b012a9bf388590c4be7f526815b5ab74feca1 #1288
+ - soname completed: 5de6848f104d7cb0017080e31216265ac19d0dde #10023
+ - bunch of others that are completed
+ - `NTLM_WB_ENABLED` is implemented in a basic form, and now also
+ scheduled for removal, so a TODO at this point isn't useful.
+
+ And this 'to-check' item:
+
+ Q: "The cmake build selected to run gcc with -fPIC on my box while the
+ plain configure script did not."
+
+ A: With CMake, since 2ebc74c36a19a1700af394c16855ce144d9878e3 #11546
+ and fc9bfb14520712672b4784e8b48256fb29204011 #11627, we explicitly
+ enable PIC for libcurl shared lib. Or when building libcurl for
+ shared and static lib in a single pass. We do this by default for
+ Windows or when enabled by the user via `SHARE_LIB_OBJECT`.
+ Otherwise we don't touch this setting. Meaning the default set by
+ CMake (if any) or the toolchain is used. On Debian Bookworm, this
+ means that PIC is disabled for static libs by default. Some platforms
+ (like macOS), has PIC enabled by default.
+ autotools supports the double-pass mode only, and in that case
+ CMake seems to match PIC behaviour now (as tested on Linux with gcc.)
+
+ Follow-up to 5d5dfdbd1a6c40bd75e982b66f49e1fa3a7eeae7 #12500
+
+ Reviewed-by: Jay Satiro
+ Closes #12509
+
+Stefan Eissing (12 Dec 2023)
+
+- CLIENT-WRITERS: design and use documentation
+
+ Closes #12507
+
+Viktor Szakats (12 Dec 2023)
+
+- cmake: delete obsolete TODO items [ci skip]
+
+ There is always room for improvement, but CMake is up to par now with
+ autotools, so there is no longer a good reason to keep around these
+ inline TODO items.
+
+ Answering one of questions:
+
+ Q: "The gcc command line use neither -g nor any -O options. As a
+ developer, I also treasure our configure scripts's --enable-debug
+ option that sets a long range of "picky" compiler options."
+
+ A: CMake offers the `CMAKE_BUILD_TYPE` variable to control debug info
+ and optimization level. E.g.:
+ - `Release` = `-O3` + no debug info
+ - `MinSizeRel` = `-Os` + no debug info
+ - `Debug` = `-O0` + debug info
+
+ https://stackoverflow.com/questions/48754619/what-are-cmake-build-type-deb
+ ug-release-relwithdebinfo-and-minsizerel/59314670#59314670
+ https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#defaul
+ t-and-custom-configurations
+
+ For picky warnings we have the `PICKY_COMPILER` options, enabled by
+ default.
+
+ Closes #12500
+
+Stefan Eissing (11 Dec 2023)
+
+- CONNECTION-FILTERS: update documentation
+
+ Closes #12497
+
+Daniel Stenberg (11 Dec 2023)
+
+- lib: reduce use of strncpy
+
+ - bearssl: select cipher without buffer copies
+ - http_aws_sigv4: avoid strncpy, require exact timestamp length
+ - http_aws_sigv4: use memcpy isntead of strncpy
+ - openssl: avoid strncpy calls
+ - schannel: check for 1.3 algos without buffer copies
+ - strerror: avoid strncpy calls
+ - telnet: avoid strncpy, return error on too long inputs
+ - vtls: avoid strncpy in multissl_version()
+
+ Closes #12499
+
+- CI/distcheck: run full tests
+
+ To be able to detect missing files better, this now runs the full CI
+ test suite. If done before, it would have detected #12462 before
+ release.
+
+ Closes #12503
+
+- docs: clean up Protocols: for cmdline options
+
+ ... and some other minor polish.
+
+ Closes #12496
+
+- cmdline/gen: fix the sorting of the man page options
+
+ They were previously sorted based on the file names, which use a .d
+ extension, making "data" get placed after "data-binary" etc. Making the
+ sort ignore the extention fixes the ordering.
+
+ Reported-by: Boris Verkhovskiy
+ Bug: https://curl.se/mail/archive-2023-12/0014.html
+ Closes #12494
+
+Daniel Gustafsson (9 Dec 2023)
+
+- doh: remove unused local variable
+
+ The nurl variable is no longer used during probing following
+ a refactoring, so remove.
+
+ Closes #12491
+
+Jay Satiro (8 Dec 2023)
+
+- build: fix Windows ADDRESS_FAMILY detection
+
+ - Include winsock2.h for Windows ADDRESS_FAMILY detection.
+
+ Prior to this change cmake detection didn't work because it included
+ ws2def.h by itself, which is missing needed types from winsock2.h.
+
+ Prior to this change autotools detection didn't work because it did not
+ include any Windows header.
+
+ In both cases libcurl would fall back on unsigned short as the address
+ family type, which is the same as ADDRESS_FAMILY.
+
+ Co-authored-by: Viktor Szakats
+
+ Closes https://github.com/curl/curl/pull/12441
+
+Daniel Stenberg (8 Dec 2023)
+
+- lib: rename Curl_strndup to Curl_memdup0 to avoid misunderstanding
+
+ Since the copy does not stop at a null byte, let's not call it anything
+ that makes you think it works like the common strndup() function.
+
+ Based on feedback from Jay Satiro, Stefan Eissing and Patrick Monnerat
+
+ Closes #12490
+
+- convsrctest.pl: removed: not used, not shipped in tarballs
+
+- tests: rename tests scripts to the test number
+
+ It is hard to name the scripts sensibly. Lots of them are similarly
+ named and the name did not tell which test that used them.
+
+ The new approach is rather to name them based on the test number that
+ runs them. Also helps us see which scripts are for individual tests
+ rather than for general test infra.
+
+ - badsymbols.pl -> test1167.pl
+ - check-deprecated.pl -> test1222.pl
+ - check-translatable-options.pl -> test1544.pl
+ - disable-scan.pl -> test1165.pl
+ - error-codes.pl -> test1175.pl
+ - errorcodes.pl -> test1477.pl
+ - extern-scan.pl -> test1135.pl
+ - manpage-scan.pl -> test1139.pl
+ - manpage-syntax.pl -> test1173.pl
+ - markdown-uppercase.pl -> test1275.pl
+ - mem-include-scan.pl -> test1132.pl
+ - nroff-scan.pl -> test1140.pl
+ - option-check.pl -> test1276.pl
+ - options-scan.pl -> test971.pl
+ - symbol-scan.pl -> test1119.pl
+ - version-scan.pl -> test1177.pl
+
+ Closes #12487
+
+Michał Antoniak (8 Dec 2023)
+
+- sendf: fix compiler warning with CURL_DISABLE_HEADERS_API
+
+ fix MSVC warning C4189: 'htype': local variable is initialized but not
+ referenced - when CURL_DISABLE_HEADERS_API is defined.
+
+ Closes #12485
+
+Viktor Szakats (8 Dec 2023)
+
+- tidy-up: whitespace
+
+ Closes #12484
+
+Stefan Eissing (7 Dec 2023)
+
+- test_02_download: fix paramters to test_02_27
+
+ - it is a special client that only ever uses http/2
+
+ Closes #12467
+
+Michał Antoniak (7 Dec 2023)
+
+- vtls: remove the Curl_cft_ssl_proxy object if CURL_DISABLE_PROXY
+
+ Closes #12459
+
+Daniel Stenberg (7 Dec 2023)
+
+- lib: strndup/memdup instead of malloc, memcpy and null-terminate
+
+ - bufref: use strndup
+ - cookie: use strndup
+ - formdata: use strndup
+ - ftp: use strndup
+ - gtls: use aprintf instead of malloc + strcpy * 2
+ - http: use strndup
+ - mbedtls: use strndup
+ - md4: use memdup
+ - ntlm: use memdup
+ - ntlm_sspi: use strndup
+ - pingpong: use memdup
+ - rtsp: use strndup instead of malloc, memcpy and null-terminate
+ - sectransp: use strndup
+ - socks_gssapi.c: use memdup
+ - vtls: use dynbuf instead of malloc, snprintf and memcpy
+ - vtls: use strdup instead of malloc + memcpy
+ - wolfssh: use strndup
+
+ Closes #12453
+
+- strdup: remove the memchr check from Curl_strndup
+
+ It makes it possible to clone a binary chunk of data.
+
+ Closes #12453
+
+- ftp: handle the PORT parsing without allocation
+
+ Also reduces amount of *cpy() calls.
+
+ Closes #12456
+
+- RELEASE-NOTES: synced
+
+ Bumped to 8.5.1
+
+- url: for disabled protocols, mention if found in redirect
+
+ To help users better understand where the URL (and denied scheme) comes
+ from. Also removed "in libcurl" from the message, since the disabling
+ can be done by the application.
+
+ The error message now says "not supported" or "disabled" depending on
+ why it was denied:
+
+ Protocol "hej" not supported
+ Protocol "http" disabled
+
+ And in redirects:
+
+ Protocol "hej" not supported (in redirect)
+ Protocol "http" disabled (in redirect)
+
+ Reported-by: Mauricio Scheffer
+ Fixes #12465
+ Closes #12469
+
+Stefan Eissing (6 Dec 2023)
+
+- sectransp_ make TLSCipherNameForNumber() available in non-verbose config
+
+ Reported-by: Cajus Pollmeier
+ Closes #12476
+ Fixes #12474
+
+YX Hao (6 Dec 2023)
+
+- lib: fix variable undeclared error caused by `infof` changes
+
+ `--disable-verbose` yields `CURL_DISABLE_VERBOSE_STRINGS` defined.
+ `infof` isn't `Curl_nop_stmt` anymore: dac293c.
+
+ Follow-up to dac293c
+
+ Closes #12470
+
+Viktor Szakats (6 Dec 2023)
+
+- tidy-up: fix yamllint whitespace issues in labeler.yml
+
+ Follow-up to bda212911457c6fadfbba50be61afc4ca513fa56 #12466
+
+ Reviewed-by: Dan Fandrich
+ Closes #12475
+
+- tidy-up: fix yamllint whitespace issues
+
+ Closes #12466
+
+Chris Sauer (6 Dec 2023)
+
+- cmake: fix typo
+
+ Follow-up to aace27b
+ Closes #12464
+
+Daniel Stenberg (6 Dec 2023)
+
+- dist: add tests/errorcodes.pl to the tarball
+
+ Used by test 1477
+
+ Reported-by: Xi Ruoyao
+ Follow-up to 0ca3a4ec9a7
+ Fixes #12462
+ Closes #12463
+
+Dan Fandrich (6 Dec 2023)
+
+- github/labeler: update a missed key in the v5 upgrade
+
+ Follow-up to ce03fe3ba
+
Version 8.5.0 (6 Dec 2023)
Daniel Stenberg (6 Dec 2023)
@@ -7979,2396 +10694,3 @@ Viktor Szakats (1 Aug 2023)
update was not tested.
Closes #11555
-
-- cmake: fixup H2 duplicate symbols for unity builds
-
- Closes #11550
-
-Pablo Busse (1 Aug 2023)
-
-- openssl: Support async cert verify callback
-
- - Update the OpenSSL connect state machine to handle
- SSL_ERROR_WANT_RETRY_VERIFY.
-
- This allows libcurl users that are using custom certificate validation
- to suspend processing while waiting for external I/O during certificate
- validation.
-
- Closes https://github.com/curl/curl/pull/11499
-
-Jay Satiro (1 Aug 2023)
-
-- tool_cb_wrt: fix invalid unicode for windows console
-
- - Suppress an incomplete UTF-8 sequence at the end of the buffer.
-
- - Attempt to reconstruct incomplete UTF-8 sequence from prior call(s)
- in current call.
-
- 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.
-
- Reported-by: Maksim Arhipov
-
- Fixes https://github.com/curl/curl/issues/9841
- Closes https://github.com/curl/curl/pull/10890
-
-Daniel Stenberg (1 Aug 2023)
-
-- sectransp: prevent CFRelease() of NULL
-
- When SecCertificateCopyCommonName() returns NULL, the common_name
- pointer remains set to NULL which apparently when calling CFRelease() on
- (sometimes?) crashes.
-
- Reported-by: Guillaume Algis
- Fixes #9194
- Closes #11554
-
-Jay Satiro (1 Aug 2023)
-
-- vtls: clarify "ALPN: offers" message
-
- Before:
- * ALPN: offers h2,http/1.1
-
- After:
- * ALPN: curl offers h2,http/1.1
-
- Bug: https://curl.se/mail/lib-2023-07/0041.html
- Reported-by: Richard W.M. Jones
- Closes #11544
-
-Daniel Stenberg (1 Aug 2023)
-
-- urlapi: make sure zoneid is also duplicated in curl_url_dup
-
- Add several curl_url_dup() tests to the general lib1560 test.
-
- Reported-by: Rutger Broekhoff
- Bug: https://curl.se/mail/lib-2023-07/0047.html
- Closes #11549
-
-Sergey (1 Aug 2023)
-
-- urlapi: fix heap buffer overflow
-
- `u->path = Curl_memdup(path, pathlen + 1);` accesses bytes after the null-ter
- minator.
-
- ```
- ==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
- ```
-
- Closes #11560
-
-Daniel Stenberg (31 Jul 2023)
-
-- curl: make %output{} in -w specify a file to write to
-
- It can be used multiple times. Use %output{>>name} to append.
-
- Add docs. Test 990 and 991 verify.
-
- Idea: #11400
- Suggested-by: ed0d2b2ce19451f2
- Closes #11416
-
-- RELEASE-NOTES: synced
-
-- tool: add "variable" support
-
- 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 (-)).
-
- 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 "\\{{".
-
- 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.
-
- Example: get the USER environment variable into the URL:
-
- --variable %USER
- --expand-url = "https://example.com/api/{{USER}}/method"
-
- 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.
-
- 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:
-
- --variable %HOME=/home/default
- --expand-variable fix@{{HOME}}/.secret
- --expand-data "{{fix:trim:url}}"
- https://example.com/
-
- Documented. Many new test cases.
-
- Co-brainstormed-by: Emanuele Torre
- Assisted-by: Jat Satiro
- Closes #11346
-
-- KNOWN_BUGS: cygwin: make install installs curl-config.1 twice
-
- Closes #8839
-
-- KNOWN_BUGS: build for iOS simulator on macOS 13.2 with Xcode 14
-
- Closes #11215
-
-- KNOWN_BUGS: cmake outputs: no version information available
-
- Closes #11158
-
-- KNOWN_BUGS: APOP authentication fails on POP3
-
- Closes #10073
-
-- KNOWN_BUGS: hyper is slow
-
- Closes #11203
-
-Patrick Monnerat (31 Jul 2023)
-
-- configure, cmake, lib: more form api deprecation
-
- Introduce a --enable-form-api configure option to control its inclusion
- in builds. The condition name defined for it is CURL_DISABLE_FORM_API.
-
- 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.
-
- "form-api" is now a new hidden test feature.
-
- Update libcurl modules to respect this option and adjust tests
- accordingly.
-
- Closes #9621
-
-Daniel Stenberg (31 Jul 2023)
-
-- mailmap: add Derzsi Dániel
-
-Derzsi Dániel (31 Jul 2023)
-
-- wolfssl: support loading system CA certificates
-
- Closes #11452
-
-Viktor Szakats (30 Jul 2023)
-
-- nss: delete more NSS references
-
- Fix the distcheck CI failure and delete more NSS references.
-
- Follow-up to 7c8bae0d9c9b2dfeeb008b9a316117d7b9675175
-
- Reviewed-by: Marcel Raad
- Reviewed-by: Daniel Stenberg
- Closes #11548
-
-Daniel Stenberg (29 Jul 2023)
-
-- nss: remove support for this TLS library
-
- Closes #11459
-
-Ryan Schmidt (29 Jul 2023)
-
-- macOS: fix target detection more
-
- 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.
-
- 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.
-
- 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.
-
- Follow-up to c73b2f82
-
- Fixes #11502
- Closes #11516
-
-Daniel Stenberg (29 Jul 2023)
-
-- tool_operate: allow SSL_CERT_FILE and SSL_CERT_DIR
-
- ... used at once.
-
- Reported-by: Gabriel Corona
- Fixes #11325
- Closes #11531
-
-Thomas M. DuBuisson (29 Jul 2023)
-
-- CI: remove Lift's configuration
-
- The Lift tool is being retired. Their site reads:
-
- "Sonatype Lift will be retiring on Sep 12, 2023, with its analysis
- stopping on Aug 12, 2023."
-
- Closes #11541
-
-Nathan Moinvaziri (29 Jul 2023)
-
-- Revert "schannel: reverse the order of certinfo insertions"
-
- This reverts commit 8986df802db9b5338d9d50a54232ebae4dbcf6dd.
-
- 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.
-
- Ref: https://github.com/curl/curl/issues/9706
-
- Closes https://github.com/curl/curl/pull/11536
-
-wangzhikun (29 Jul 2023)
-
-- winbuild: improve check for static zlib
-
- - Check for zlib static library name zlibstatic.lib.
-
- 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).
-
- Prior to this change the makefile only checked for the latter.
-
- Closes https://github.com/curl/curl/pull/11521
-
-Daniel Stenberg (29 Jul 2023)
-
-- configure: use the pkg-config --libs-only-l flag for libssh2
-
- ... instead of --libs, as that one also returns -L flags.
-
- Reported-by: Wilhelm von Thiele
- Fixes #11538
- Closes #11539
-
-Viktor Szakats (29 Jul 2023)
-
-- cmake: support building static and shared libcurl in one go
-
- 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.
-
- 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.
-
- Also:
-
- - 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`.
-
- - 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.
-
- - Convert an existing CI test to build both shared and static libcurl.
-
- Closes #11505
-
-Stefan Eissing (28 Jul 2023)
-
-- CI/awslc: add cache for build awslc library
-
- Closes #11535
-
-- GHA/linux.yml: add caching
-
- Closes #11532
-
-Daniel Stenberg (27 Jul 2023)
-
-- RELEASE-NOTES: synced
-
- Bump working version to 8.3.0
-
-- url: remove infof() output for "still name resolving"
-
- The message does not help and might get spewed a lot during times.
-
- Reported-by: yushicheng7788 on github
- Fixes #11394
- Closes #11529
-
-- KNOWN_BUGS: cygwin: "WARNING: UNPROTECTED PRIVATE KEY FILE!"
-
- Closes #11244
-
-Stefan Eissing (27 Jul 2023)
-
-- CI: quiche updates
-
- - 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 #11528
-
-- http2: disable asssertion blocking OSSFuzz testing
-
- - 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.
-
- Fixes #11500
- Closes #11519
-
-- http2: fix in h2 proxy tunnel: progress in ingress on sending
-
- - 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.
-
- CI:
- - add a chache for building mod_h2
-
- Closes #11527
-
-- CI ngtcp2+quictls: use nghttpx cache as in quiche build
-
-Jay Satiro (27 Jul 2023)
-
-- bearssl: don't load CA certs when peer verification is disabled
-
- We already do this for other SSL backends.
-
- Bug: https://github.com/curl/curl/pull/11457#issuecomment-1644587473
- Reported-by: kyled-dell@users.noreply.github.com
-
- Closes https://github.com/curl/curl/pull/11497
-
-Daniel Stenberg (26 Jul 2023)
-
-- easy: remove #ifdefs to make code easier on the eye
-
- Closes #11525
-
-Stefan Eissing (26 Jul 2023)
-
-- GHA: adding quiche workflow
-
- - adding separate quiche workflow to also build nghttpx server for testing
-
- Closes #11517
-
-Version 8.2.1 (26 Jul 2023)
-
-Daniel Stenberg (26 Jul 2023)
-
-- RELEASE-NOTES: synced
-
- curl 8.2.1 release
-
-- THANKS: add contributors from 8.2.1
-
-- docs: provide more see also for cipher options
-
- More cross references. Hide nroff errors.
-
- Closes #11513
-
-- docs: mark two TLS options for TLS, not SSL
-
- Closes #11514
-
-Brad Harder (25 Jul 2023)
-
-- curl_multi_wait.3: fix arg quoting to doc macro .BR
-
- Closes #11511
-
-Daniel Stenberg (24 Jul 2023)
-
-- RELEASE-NOTES: synced
-
-Viktor Szakats (24 Jul 2023)
-
-- cmake: update ngtcp2 detection
-
- Replace `OpenSSL` with `quictls` to follow the same change
- in the v0.17.0 ngtcp2 release.
-
- Follow-up to e0093b4b732f6495b0fb1cd6747cbfedcdcf63ed
-
- Closes #11508
-
-Stefan Eissing (24 Jul 2023)
-
-- http: VLH, very large header test and fixes
-
- - 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
-
- Closes #11509
-
-Andrei Rybak (23 Jul 2023)
-
-- CONTRIBUTE: drop mention of copyright year ranges
-
- 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.
-
- Drop mentions of copyright year ranges from 'docs/CONTRIBUTE.md'.
-
- [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)
-
- Closes #11504
-
-- CONTRIBUTE: fix syntax in commit message description
-
- 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.
-
- Fix the enclosing square brackets in description of "Signed-off-by"
- trailers in commit messages in file 'docs/CONTRIBUTE.md'.
-
- [1] See description of option '--signoff' in Git documentation:
- https://git-scm.com/docs/git-commit
-
- Closes #11504
-
-Daniel Stenberg (23 Jul 2023)
-
-- src/mkhelp: strip off escape sequences
-
- At some point the nroff command stopped stripping off escape sequences,
- so then this script needs to do the job instead.
-
- Reported-by: VictorVG on github
- Fixes #11501
- Closes #11503
-
-- KNOWN_BUGS: building for old macOS fails with gcc
-
- Closes #11441
-
-Jacob Hoffman-Andrews (22 Jul 2023)
-
-- rustls: update rustls-ffi 0.10.0
-
- This brings in version 0.21.0 of the upstream rustls implementation,
- which notable includes support for IP address certificates.
-
- Closes #10865
-
-Brad Harder (22 Jul 2023)
-
-- websocket: rename arguments/variables to match docs
-
- Pedantry/semantic-alignment between functions, docs, comments with
- respect to websocket protocol code; No functional change intended.
-
- * "totalsize", "framesize" becomes "fragsize" (we deal in frame fragments).
-
- * "sendflags" becomes "flags"
-
- * use canonical CURL *handle
-
- Closes #11493
-
-Jan Macku (21 Jul 2023)
-
-- bug_report: use issue forms instead of markdown template
-
- 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.
-
- Signed-off-by: Jan Macku <jamacku@redhat.com>
- Closes #11474
-
-Daniel Stenberg (21 Jul 2023)
-
-- TODO: Obey Retry-After in redirects
-
- (remove "Set custom client ip when using haproxy protocol" which was
- shipped in 8.2.0)
-
- Mentioned-by: Yair Lenga
- Closes #11447
-
-- RELEASE-NOTES: synced
-
-Oliver Roberts (21 Jul 2023)
-
-- amissl: fix AmiSSL v5 detection
-
- Due to changes in the AmiSSL SDK, the detection needed adjusting.
-
- Closes #11477
-
-Alois Klink (21 Jul 2023)
-
-- unittest/makefile: remove unneeded unit1621_LDADD
-
- The `unit1621_LDADD` variable has the exact same value as the `LDADD`
- flag in `Makefile.am`, except without `@LDFLAGS@ @LIBCURL_LIBS@`.
-
- 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.
-
- [98e6629]: https://github.com/curl/curl/commit/98e6629154044e4ab1ee7cff8351c7
- ebcb131e88
-
- Closes #11494
-
-- unittest/makefile: remove unneeded unit1394_LDADD
-
- 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.
-
- [8dac7be]: https://github.com/curl/curl/commit/8dac7be438512a8725d3c71e9139bd
- fdcac1ed8c
- [265b14d]: https://github.com/curl/curl/commit/265b14d6b37c4298bd5556fabcbc37
- d36f911693
-
- Closes #11494
-
-- cmake: add `libcurlu`/`libcurltool` for unit tests
-
- 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.
-
- These libraries allow us to compile every unit test with CMake.
-
- Closes #11446
-
-Daniel Stenberg (21 Jul 2023)
-
-- test979: test -u with redirect to (the same) absolute host
-
- Verifies #11492
-
-- transfer: do not clear the credentials on redirect to absolute URL
-
- Makes test 979 work. Regression shipped in 8.2.0 from commit
- dd4d1a26959f63a2c
-
- Fixes #11486
- Reported-by: Cloudogu Siebels
- Closes #11492
-
-Jon Rumsey (20 Jul 2023)
-
-- os400: correct EXPECTED_STRING_LASTZEROTERMINATED
-
- Correct EXPECTED_STRING_LASTZEROTERMINATED to account for
- CURLOPT_HAPROXY_CLIENT_IP which requires EBCDIC to ASCII conversion when
- passed into curl_easy_setopt().
-
- Closes #11476
-
-Oliver Roberts (20 Jul 2023)
-
-- amissl: add missing signal.h include
-
- In some environments, signal.h is already included, but not in others
- which cause compilation to fail, so explictly include it.
-
- Closes #11478
-
-- amigaos: fix sys/mbuf.h m_len macro clash
-
- 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 #11479
-
-Daniel Stenberg (20 Jul 2023)
-
-- socks: print ipv6 address within brackets
-
- Fixes #11483
- Closes #11484
-
-Christian Schmitz (20 Jul 2023)
-
-- libcurl-errors.3: add CURLUE_OK
-
- Closes #11488
-
-Oliver Roberts (20 Jul 2023)
-
-- cfilters: rename close/connect functions to avoid clashes
-
- 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).
-
- Closes #11491
-
-Stefan Eissing (20 Jul 2023)
-
-- http2: fix regression on upload EOF handling
-
- - 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
-
- Fixes #11485
- Closes #11487
- Reported-by: Aleksander Mazur
-
-Daniel Stenberg (19 Jul 2023)
-
-- configure: check for nghttp2_session_get_stream_local_window_size
-
- The http2 code uses it now. Introduced in nghttp2 1.15.0 (Sep 2016)
-
- Fixes #11470
- Reported-by: Paul Howarth
- Closes #11473
-
-Stefan Eissing (19 Jul 2023)
-
-- quiche: fix segfault and other things
-
- - 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.
-
- Fixes #11449
- Closes #11469
- Reported-by: ウさん
-
-Daniel Stenberg (19 Jul 2023)
-
-- hostip: return IPv6 first for localhost resolves
-
- Fixes #11465
- Reported-by: Chilledheart on github
- Closes #11466
-
-Harry Sintonen (19 Jul 2023)
-
-- tool: fix tool_seek_cb build when SIZEOF_CURL_OFF_T > SIZEOF_OFF_T
-
- - a variable was renamed, and some use of it wasn't. this fixes the
- build.
-
- Closes #11468
-
-Stefan Eissing (19 Jul 2023)
-
-- quiche: fix lookup of transfer at multi
-
- - 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
-
- Closes #11462
-
-Daniel Stenberg (19 Jul 2023)
-
-- RELEASE-NOTES: synced
-
- bump to 8.2.1
-
-John Haugabook (19 Jul 2023)
-
-- ciphers.d: put URL in first column
-
- This makes the URL turn into a link properly when "webified".
-
- Fixes https://github.com/curl/curl-www/issues/270
- Closes #11464
-
-Version 8.2.0 (19 Jul 2023)
-
-Daniel Stenberg (19 Jul 2023)
-
-- RELEASE-NOTES: synced
-
- 8.2.0 release
-
-- THANKS-filter: strip out "GitHub"
-
-- THANKS: add contributors from 8.2.0
-
-- RELEASE-PROCEDURE.md: adjust the release dates
-
-Stefan Eissing (17 Jul 2023)
-
-- quiche: fix defects found in latest coverity report
-
- Closes #11455
-
-Daniel Stenberg (17 Jul 2023)
-
-- quiche: avoid NULL deref in debug logging
-
- Coverity reported "Dereference after null check"
-
- If stream is NULL and the function exits, the logging must not deref it.
-
- Closes #11454
-
-Stefan Eissing (17 Jul 2023)
-
-- http2: treat initial SETTINGS as a WINDOW_UPDATE
-
- - 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
-
- Closes #11450
-
-Daniel Stenberg (17 Jul 2023)
-
-- ngtcp2: assigning timeout, but value is overwritten before used
-
- Reported by Coverity
-
- Closes #11453
-
-- krb5: add typecast to please Coverity
-
-Derzsi Dániel (16 Jul 2023)
-
-- wolfssl: support setting CA certificates as blob
-
- Closes #11445
-
-- wolfssl: detect when TLS 1.2 support is not built into wolfssl
-
- Closes #11444
-
-Graham Campbell (15 Jul 2023)
-
-- CI: bump nghttp2 from 1.55.0 to 1.55.1
-
- Closes #11442
-
-Daniel Stenberg (15 Jul 2023)
-
-- curl: return error when asked to use an unsupported HTTP version
-
- When one of the following options are used but the libcurl in use does
- not support it:
-
- --http2
- --http2-prior-knowledge
- --proxy-http2
-
- Closes #11440
-
-Chris Paulson-Ellis (14 Jul 2023)
-
-- cf-socket: don't bypass fclosesocket callback if cancelled before connect
-
- 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.
-
- 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.
-
- 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 <stefan@eissing.org>
- Date: Fri Dec 30 09:14:55 2022 +0100
-
- lib: connect/h2/h3 refactor
-
- Closes #11439
-
-Daniel Stenberg (13 Jul 2023)
-
-- tool_parsecfg: accept line lengths up to 10M
-
- Bumped from 100K set in 47dd957daff9
-
- Reported-by: Antoine du Hamel
- Fixes #11431
- Closes #11435
-
-Stefan Eissing (13 Jul 2023)
-
-- CI: brew fix for openssl in default path
-
- 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.
-
- Fixes #11413
- Closes #11436
-
-Daniel Stenberg (13 Jul 2023)
-
-- RELEASE-NOTES: synced
-
-Ondřej Koláček (13 Jul 2023)
-
-- sectransp: fix EOF handling
-
- Regression since the large refactor from 2022
-
- Closes #11427
-
-Daniel Stenberg (13 Jul 2023)
-
-- checksrc: quote the file name to work with "funny" letters
-
- Closes #11437
-
-Karthikdasari0423 (13 Jul 2023)
-
-- HTTP3.md: ngtcp2 updated to v0.17.0 and nghttp3 to v0.13.0
-
- Follow-up to e0093b4b732f6
-
- Closes #11433
-
-Daniel Stenberg (13 Jul 2023)
-
-- CURLOPT_MIMEPOST.3: clarify what setting to NULL means
-
- Follow-up to e08382a208d4e480
-
- Closes #11430
-
-Tatsuhiro Tsujikawa (12 Jul 2023)
-
-- ngtcp2: build with 0.17.0 and nghttp3 0.13.0
-
- - ngtcp2_crypto_openssl was renamed to ngtcp2_crypto_quictls.
-
- Closes #11428
-
-- CI: Bump ngtcp2, nghttp3, and nghttp2
-
- Closes #11428
-
-James Fuller (11 Jul 2023)
-
-- example/maxconnects: set maxconnect example
-
- Closes #11343
-
-Pontakorn Prasertsuk (11 Jul 2023)
-
-- http2: send HEADER & DATA together if possible
-
- Closes #11420
-
-Daniel Stenberg (11 Jul 2023)
-
-- CI: use wolfSSL 5.6.3 in builds
-
- No using master anymore
-
- Closes #11424
-
-SaltyMilk (11 Jul 2023)
-
-- fopen: optimize
-
- Closes #11419
-
-Daniel Stenberg (11 Jul 2023)
-
-- cmake: make use of snprintf
-
- Follow-up to 935b1bd4544a23a91d68
-
- Closes #11423
-
-Stefan Eissing (11 Jul 2023)
-
-- macOS: fix taget detection
-
- - 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
-
- Follow-up to c7308592fb8ba213fc2c1
- Closes #11417
-
-Stan Hu (9 Jul 2023)
-
-- hostip.c: Move macOS-specific calls into global init call
-
- 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.
-
- 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.
-
- In addition, this change is beneficial because it:
-
- 1. Avoids extra macOS system calls for every IP lookup.
- 2. Consolidates macOS-specific initialization in a separate file.
-
- Fixes #11252
- Closes #11254
-
-Daniel Stenberg (9 Jul 2023)
-
-- docs: use a space after RFC when spelling out RFC numbers
-
- Closes #11382
-
-Margu (9 Jul 2023)
-
-- imap-append.c: update to make it more likely to work
-
- Fixes #10300
- Closes #11397
-
-Emanuele Torre (9 Jul 2023)
-
-- tool_writeout_json: fix encoding of control characters
-
- Control characters without a special escape sequence e.g. %00 or %06
- were being encoded as "u0006" instead of "\u0006".
-
- Ref: https://github.com/curl/trurl/pull/214#discussion_r1257487858
- Closes #11414
-
-Stefan Eissing (9 Jul 2023)
-
-- http3/ngtcp2: upload EAGAIN handling
-
- - 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
-
- Fixes #11389
- Closes #11390
-
-Brian Nixon (9 Jul 2023)
-
-- tool_easysrc.h: correct `easysrc_perform` for `CURL_DISABLE_LIBCURL_OPTION`
-
- Closes #11398
-
-Daniel Stenberg (9 Jul 2023)
-
-- RELEASE-NOTES: synced
-
-- transfer: clear credentials when redirecting to absolute URL
-
- Make sure the user and password for the second request is taken from the
- redirected-to URL.
-
- Add test case 899 to verify.
-
- Reported-by: James Lucas
- Fixes #11410
- Closes #11412
-
-Stefan Eissing (8 Jul 2023)
-
-- hyper: fix EOF handling on input
-
- We ran out of disc space due to an infinite loop with debug logging
-
- Fixes #11377
- Closes #11385
- Reported-by: Dan Fandrich
-
-- http2: raise header limitations above and beyond
-
- - 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.
-
- Fixes #11405
- Closes #11407
-
-Juan Cruz Viotti (8 Jul 2023)
-
-- curl_easy_nextheader.3: add missing open parenthesis examples
-
- Closes #11409
- Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
-
-Dan Fandrich (7 Jul 2023)
-
-- CI: enable verbose test output on pytest
-
- This shows individual pass/fail status on tests and makes this output
- consistent with other jobs' pytest invocations.
-
-Stefan Eissing (28 Jun 2023)
-
-- http2: fix crash in handling stream weights
-
- - Delay the priority handling until the stream has been opened.
-
- - Add test2404 to reproduce and verify.
-
- 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.
-
- Reported-by: jbgoog@users.noreply.github.com
-
- Fixes https://github.com/curl/curl/issues/11379
- Closes https://github.com/curl/curl/pull/11384
-
-- tests/http: Add mod_h2 directive `H2ProxyRequests`
-
- master of mod_h2 now requires H2ProxyRequests directives for forward
- proxying with HTTP/2 to work.
-
- Ref: https://github.com/icing/mod_h2/commit/3897a7086
-
- Closes https://github.com/curl/curl/pull/11392
-
-Dan Fandrich (28 Jun 2023)
-
-- CI: make Appveyor job names unique
-
- Two otherwise identical mingw-w64 jobs now have their differing compiler
- versions mentioned in their names.
-
-Sheshadri.V (25 Jun 2023)
-
-- curl.h: include <sys/select.h> for vxworks
-
- Closes #11356
-
-Dan Fandrich (24 Jun 2023)
-
-- CI: enable parallel make in more builds
-
- 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.
-
-- CI: don't install impacket if tests are not run
-
- It just wastes time and bandwidth and isn't even used.
-
-divinity76 (24 Jun 2023)
-
-- configure: the --without forms of the options are also gone
-
- --without-darwin-ssl and --without-metalink
-
- Closes #11378
-
-Daniel Stenberg (23 Jun 2023)
-
-- configure: add check for ldap_init_fd
-
- ... 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.
-
- Regression since 4d7385446 (7.85.0)
- Fixes #11372
- Closes #11374
- Reported-by: vlkl-sap on github
-
-Michał Petryka (23 Jun 2023)
-
-- cmake: stop CMake from quietly ignoring missing Brotli
-
- 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.
-
- Closes #11376
-
-Emanuele Torre (22 Jun 2023)
-
-- docs: add more .IP after .RE to fix indentation of generate paragraphs
-
- follow-up from 099f41e097c030077b8ec078f2c2d4038d31353b
-
- 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 #11375
-
-Stefan Eissing (22 Jun 2023)
-
-- http2: h2 and h2-PROXY connection alive check fixes
-
- - 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
-
- Closes #11368
-
-- http2: error stream resets with code CURLE_HTTP2_STREAM
-
- - 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
-
- Fixes #11357
- Closes #11362
- Reported-by: Jay Satiro
-
-Daniel Stenberg (22 Jun 2023)
-
-- connect-timeout.d: mention that the DNS lookup is included
-
- Closes #11370
-
-Emanuele Torre (22 Jun 2023)
-
-- quote.d: fix indentation of generated paragraphs
-
- 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.
-
- 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.
-
- Closes #11371
-
-Paul Wise (22 Jun 2023)
-
-- checksrc: modernise perl file open
-
- Use regular variables and separate file open modes from filenames.
-
- Suggested by perlcritic
-
- Copied from https://github.com/curl/trurl/commit/f2784a9240f47ee28a845
-
- Closes #11358
-
-Dan Fandrich (21 Jun 2023)
-
-- runtests: work around a perl without SIGUSR1
-
- 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.
-
- Reported-by: Marcel Raad
- Fixes #11350
- Closes #11366
-
-- runtests: include missing valgrind package
-
- use valgrind was missing which caused torture tests with valgrind
- enabled to fail.
-
- Reported-by: Daniel Stenberg
- Fixes #11364
- Closes #11365
-
-- runtests: use more consistent failure lines
-
- 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.
-
-- runtests: consistently write the test check summary block
-
- 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.
-
-- test2600: fix the description
-
- It looks like it was cut-and-pasted.
-
- Closes #11354
-
-Daniel Stenberg (21 Jun 2023)
-
-- TODO: "Support HTTP/2 for HTTP(S) proxies" *done*
-
-humbleacolyte (21 Jun 2023)
-
-- cf-socket: move ctx declaration under HAVE_GETPEERNAME
-
- Closes #11352
-
-Daniel Stenberg (20 Jun 2023)
-
-- RELEASE-NOTES: synced
-
-- example/connect-to: show CURLOPT_CONNECT_TO
-
- Closes #11340
-
-Stefan Eissing (20 Jun 2023)
-
-- hyper: unslow
-
- - 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.
-
- Fixes #11203
- Reported-by: Gisle Vanem
- Advised-by: Sean McArthur
- Closes #11344
-
-- HTTP/2: upload handling fixes
-
- - 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
-
- Reported-by: Sergey Alirzaev
- Fixed #11242
- Closes #11342
-
-Daniel Stenberg (20 Jun 2023)
-
-- example/opensslthreadlock: remove
-
- 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
-
- Closes #11341
-
-Dan Fandrich (19 Jun 2023)
-
-- libtest: display the times after a test timeout error
-
- This is to help with test failure debugging.
-
- Ref: #11328
- Closes #11329
-
-- test2600: bump a test timeout
-
- Case 1 failed at least once on GHA by going 30 msec too long.
-
- Ref: #11328
-
-- runtests: better detect and handle pipe errors in the controller
-
- 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.
-
-- runtests: cleanly abort the runner if the controller dies
-
- If the controller dies unexpectedly, have the runner stop its servers
- and exit cleanly. Otherwise, the orphaned servers will stay running in
- the background.
-
-- runtests: improve error logging
-
- 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.
-
- Ref: #11328
-
-- runtests: better handle ^C during slow tests
-
- 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.
-
-- runtests: rename server command file
-
- 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.
-
-- tests: improve reliability of TFTP tests
-
- 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.
-
- 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.
-
- Ref: #11328
-
-Daniel Stenberg (19 Jun 2023)
-
-- cf-socket: skip getpeername()/getsockname for TFTP
-
- 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.
-
- This could be seen in stderr for test 1007
-
- Test 1007 has now been extended to verify the stderr message.
-
- Closes #11332
-
-- example/crawler: make it use a few more options
-
- For show, but reasonable
-
-- libcurl-ws.3: mention raw mode
-
- Closes #11339
-
-- example/default-scheme: set the default scheme for schemeless URLs
-
- Closes #11338
-
-- example/hsts-preload: show one way to HSTS preload
-
- Closes #11337
-
-- examples/http-options: show how to send "OPTIONS *"
-
- With CURLOPT_REQUEST_TARGET.
-
- Also add use of CURLOPT_QUICK_EXIT to show.
-
- Closes #11333
-
-- examples: make use of CURLOPT_(REDIR_|)PROTOCOLS_STR
-
- To show how to use them
-
- Closes #11334
-
-- examples/smtp-mime: use CURLOPT_MAIL_RCPT_ALLOWFAILS
-
- For show
-
- Closes #11335
-
-- http: rectify the outgoing Cookie: header field size check
-
- 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.
-
- This could make the check be off by several hundred bytes in some cases.
-
- Closes #11331
-
-Jay Satiro (17 Jun 2023)
-
-- lib: fix some format specifiers
-
- - Use CURL_FORMAT_CURL_OFF_T where %zd was erroneously used for some
- curl_off_t variables.
-
- - Use %zu where %zd was erroneously used for some size_t variables.
-
- 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.
-
- Fixes https://github.com/curl/curl/issues/11327
- Closes https://github.com/curl/curl/pull/11321
-
-Marcel Raad (16 Jun 2023)
-
-- test427: add `cookies` feature and keyword
-
- This test doesn't work with `--disable-cookies`.
-
- Closes https://github.com/curl/curl/pull/11320
-
-Chris Talbot (15 Jun 2023)
-
-- imap: Provide method to disable SASL if it is advertised
-
- - Implement AUTH=+LOGIN for CURLOPT_LOGIN_OPTIONS to prefer plaintext
- LOGIN over SASL auth.
-
- 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.
-
- Per: https://www.ietf.org/rfc/rfc5092.html#section-3.2
-
- ";AUTH=<enc-auth-type>" looks to be the correct way to specify what
- authenication method to use, regardless of SASL or not.
-
- Closes https://github.com/curl/curl/pull/10041
-
-Daniel Stenberg (15 Jun 2023)
-
-- RELEASE-NOTES: synced
-
-- examples/multi-debugcallback.c: avoid the bool typedef
-
- Apparently this cannot be done in c23
-
- Reported-by: Cristian Rodríguez
- Fixes #11299
- Closes #11319
-
-- docs/libcurl/libcurl.3: cleanups and improvements
-
- Closes #11317
-
-- libcurl-ws.3: fix typo
-
-- curl_ws_*.3: enhance
-
- - all: SEE ALSO the libcurl-ws man page
- - send: add example and return value information
- - meta: mention that the returned data is read-only
-
- Closes #11318
-
-- docs/libcurl/libcurl-ws.3: see also CURLOPT_WS_OPTIONS
-
-- docs/libcurl/libcurl-ws.3: minor polish
-
-- libcurl-ws.3. WebSocket API overview
-
- Closes #11314
-
-- libcurl-url.3: also mention CURLUPART_ZONEID
-
- ... and sort the two part-using lists alphabetically
-
-Marcel Raad (14 Jun 2023)
-
-- fopen: fix conversion warning on 32-bit Android
-
- 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.
-
- 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
-
-- http2: fix variable type
-
- `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 https://github.com/curl/curl/pull/11312
-
-Daniel Stenberg (13 Jun 2023)
-
-- vtls: fix potentially uninitialized local variable warnings
-
- Follow-up from a4a5e438ae533c
-
- Closes #11310
-
-- timeval: use CLOCK_MONOTONIC_RAW if available
-
- Reported-by: Harry Sintonen
- Ref: #11288
- Closes #11291
-
-Stefan Eissing (12 Jun 2023)
-
-- tool: add curl command line option `--trace-ids`
-
- - 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.
-
- Closes #11185
-
-- lib: add CURLINFO_CONN_ID and CURLINFO_XFER_ID
-
- - 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
-
- Closes #11185
-
-Daniel Stenberg (12 Jun 2023)
-
-- CURLOPT_INFILESIZE.3: mention -1 triggers chunked
-
- Ref: #11300
- Closes #11304
-
-Philip Heiduck (12 Jun 2023)
-
-- CI: openssl-3.0.9+quic
-
- Closes #11296
-
-Karthikdasari0423 (12 Jun 2023)
-
-- HTTP3.md: update openssl version
-
- Closes #11297
-
-Daniel Stenberg (12 Jun 2023)
-
-- vtls: avoid memory leak if sha256 call fails
-
- ... in the pinned public key handling function.
-
- Reported-by: lizhuang0630 on github
- Fixes #11306
- Closes #11307
-
-- examples/ipv6: disable on win32
-
- I can't make if_nametoindex() work there
-
- Follow-up to c23dc42f3997acf23
-
- Closes #11305
-
-- tool_operate: allow cookie lines up to 8200 bytes
-
- 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.
-
- Closes #11303
-
-- test427: verify sending more cookies than fit in a 8190 bytes line
-
- curl will then only populate the header with cookies that fit, dropping
- ones that otherwise would have been sent
-
- Ref: https://curl.se/mail/lib-2023-06/0020.html
-
- Closes #11303
-
-- testutil: allow multiple %-operators on the same line
-
- Closes #11303
-
-Oleg Jukovec (12 Jun 2023)
-
-- docs: update CURLOPT_UPLOAD.3
-
- 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.
-
- Closes #11300
-
-Daniel Stenberg (12 Jun 2023)
-
-- RELEASE-NOTES: synced
-
-- CURLOPT_AWS_SIGV4.3: remove unused variable from example
-
- Closes #11302
-
-- examples/https.c: use CURLOPT_CA_CACHE_TIMEOUT
-
- for demonstration purposes
-
- Closes #11290
-
-- example/ipv6: feature CURLOPT_ADDRESS_SCOPE in use
-
- Closes #11282
-
-Karthikdasari0423 (10 Jun 2023)
-
-- docs: Update HTTP3.md for newer ngtcp2 and nghttp3
-
- Follow-up to fb9b9b58
-
- Ref: #11184
- Closes #11295
-
-Dan Fandrich (10 Jun 2023)
-
-- docs: update the supported ngtcp2 and nghttp3 versions
-
- Follow-up to cae9d10b
-
- Ref: #11184
- Closes #11294
-
-- tests: fix error messages & handling around sockets
-
- 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.
-
- Ref #11258
- Closes #11265
-
-Daniel Stenberg (9 Jun 2023)
-
-- CURLOPT_SSH_PRIVATE_KEYFILE.3: expand on the file search
-
- Reported-by: atjg on github
- Ref: #11287
- Closes #11289
-
-Stefan Eissing (9 Jun 2023)
-
-- ngtcp2: use ever increasing timestamp in io
-
- - 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.
-
- Closes #11288
-
-Daniel Stenberg (9 Jun 2023)
-
-- GHA: use nghttp2 1.54.0 for the ngtcp2 jobs
-
-Philip Heiduck (9 Jun 2023)
-
-- GHA: ngtcp2: use 0.16.0 and nghttp3 0.12.0
-
-Daniel Stenberg (9 Jun 2023)
-
-- ngtcp2: build with 0.16.0 and nghttp3 0.12.0
-
- - 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 #11184
-
-- example/http2-download: set CURLOPT_BUFFERSIZE
-
- Primarily because no other example sets it, and remove the disabling of
- the certificate check because we should not recommend that.
-
- Closes #11284
-
-- example/crawler: also set CURLOPT_AUTOREFERER
-
- Could make sense, and it was not used in any example before.
-
- Closes #11283
-
-Wyatt OʼDay (9 Jun 2023)
-
-- tls13-ciphers.d: include Schannel
-
- Closes #11271
-
-Daniel Stenberg (9 Jun 2023)
-
-- curl_pushheader_byname/bynum.3: document in their own man pages
-
- 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.
-
- It makes better sense and gives more visibility to document them in
- their own stand-alone man pages.
-
- Closes #11286
-
-- curl_mprintf.3: minor fix of the example
-
-- curl_url_set: enforce the max string length check for all parts
-
- Update the docs and test 1559 accordingly
-
- Closes #11273
-
-- examples/ftpuploadresume.c: add use of CURLOPT_ACCEPTTIMEOUT_MS
-
- For show
-
- Closes #11277
-
-- examples/unixsocket.c: example using CURLOPT_UNIX_SOCKET_PATH
-
- and alternatively CURLOPT_ABSTRACT_UNIX_SOCKET
-
- Closes #11276
-
-Anssi Kolehmainen (8 Jun 2023)
-
-- docs: fix missing parameter names in examples
-
- Closes #11278
-
-Daniel Stenberg (8 Jun 2023)
-
-- urlapi: have *set(PATH) prepend a slash if one is missing
-
- 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.
-
- Amend lib1560 to verify this. Clarify the curl_url_set() docs about it.
-
- Bug: https://curl.se/mail/lib-2023-06/0015.html
- Closes #11272
- Reported-by: Pedro Henrique
-
-Dan Fandrich (7 Jun 2023)
-
-- runtests; give each server a unique log lock file
-
- 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.
-
- 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.
-
- Fixes #11231
- Closes #11259
-
-- runtests: make test file directories in log/N
-
- Test files in subdirectories were not created after parallel test log
- directories were moved down a level due to a now-bad comparison.
-
- Follow-up to 92d7dd39
-
- Ref #11264
- Closes #11267
-
-Daniel Stenberg (7 Jun 2023)
-
-- ws: make the curl_ws_meta() return pointer a const
-
- The returned info is read-only for the user.
-
- Closes #11261
-
-- RELEASE-NOTES: synced
-
-- runtests: move parallel log dirs from logN to log/N
-
- Having several hundreds of them in there gets annoying.
-
- Closes #11264
-
-Dan Fandrich (7 Jun 2023)
-
-- test447: move the test file into %LOGDIR
-
-Viktor Szakats (7 Jun 2023)
-
-- cmake: add support for "unity" builds
-
- Aka "jumbo" or "amalgamation" builds. It means to compile all sources
- per target as a single C source. This is experimental.
-
- You can enable it by passing `-DCMAKE_UNITY_BUILD=ON` to cmake.
- It requires CMake 3.16 or newer.
-
- It makes builds (much) faster, allows for better optimizations and tends
- to promote less ambiguous code.
-
- 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.
-
- 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 `<subauth.h>` 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.
-
- 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
-
- 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
-
- Closes #11095
-
-Daniel Stenberg (7 Jun 2023)
-
-- examples/websocket.c: websocket example using CONNECT_ONLY
-
- Closes #11262
-
-- websocket-cb: example doing WebSocket download using callback
-
- Very basic
-
- Closes #11260
-
-- test/.gitignore: ignore log*
-
-Dan Fandrich (5 Jun 2023)
-
-- runtests: document the -j parallel testing option
-
- Reported-by: Daniel Stenberg
- Ref: #10818
- Closes #11255
-
-- runtests: create multiple test runners when requested
-
- 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.
-
- Flaky tests due to improper log locking (bug #11231) are exacerbated
- while parallel testing, so it is not enabled by default yet.
-
- Fixes #10818
- Closes #11246
-
-- runtests: handle repeating tests in multiprocess mode
-
- 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.
-
- Ref: #10818
-
-- runtests: buffer logmsg while running singletest()
-
- This allows all messages relating to a single test case to be displayed
- together at the end of the test.
-
- Ref: #10818
-
-- runtests: call initserverconfig() in the runner
-
- This must be done so variables pick up the runner's unique $LOGDIR.
-
- Ref: #10818
-
-- runtests: use a per-runner random seed
-
- 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.
-
- Ref: #10818
-
-- runtests: complete main test loop refactor for multiple runners
-
- 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.
-
- Ref: #10818
-
-- runtests: prepare main test loop for multiple runners
-
- Some variables are expanded to arrays and hashes so that multiple
- runners can be used for running tests.
-
- Ref: #10818
-
-Stefan Eissing (5 Jun 2023)
-
-- bufq: make write/pass methods more robust
-
- - related to #11242 where curl enters busy loop when
- sending http2 data to the server
-
- Closes #11247
-
-Boris Verkhovskiy (5 Jun 2023)
-
-- tool_getparam: fix comment
-
- Closes #11253
-
-Raito Bezarius (5 Jun 2023)
-
-- haproxy: add --haproxy-clientip flag to spoof client IPs
-
- CURLOPT_HAPROXY_CLIENT_IP in the library
-
- Closes #10779
-
-Daniel Stenberg (5 Jun 2023)
-
-- curl: add --ca-native and --proxy-ca-native
-
- 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.
-
- They currently only have an effect for curl on Windows when built to use
- OpenSSL for TLS.
-
- Closes #11049
-
-Viktor Szakats (5 Jun 2023)
-
-- build: drop unused/redundant `HAVE_WINLDAP_H`
-
- Sources did not use it. Autotools used it when checking for the
- `winldap` library, which is redundant.
-
- 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
- ```
-
- Cherry-picked from #11095 88e4a21ff70ccef391cf99c8165281ff81374503
- Reviewed-by: Daniel Stenberg
- Closes #11245
-
-Daniel Stenberg (5 Jun 2023)
-
-- urlapi: scheme starts with alpha
-
- Add multiple tests to lib1560 to verify
-
- Fixes #11249
- Reported-by: ad0p on github
- Closes #11250
-
-- RELEASE-NOTES: synced
-
-- CURLOPT_MAIL_RCPT_ALLOWFAILS: replace CURLOPT_MAIL_RCPT_ALLLOWFAILS
-
- Deprecate the name using three Ls and prefer the name with two.
-
- Replaces #10047
- Closes #11218
-
-- tests/servers: generate temp names in /tmp for unix domain sockets
-
- ... 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.
-
- The new concept generates two random names: one for the socks daemon and
- one for http.
-
- Reported-by: Andy Fiddaman
- Fixes #11152
- Closes #11166
-
-Stefan Eissing (2 Jun 2023)
-
-- http2: better support for --limit-rate
-
- - 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
-
- Closes #11115
-
-- curl_log: evaluate log statement only when transfer is verbose
-
- Closes #11238
-
-Daniel Stenberg (2 Jun 2023)
-
-- libssh2: provide error message when setting host key type fails
-
- Ref: https://curl.se/mail/archive-2023-06/0001.html
-
- Closes #11240
-
-Igor Todorovski (2 Jun 2023)
-
-- system.h: remove __IBMC__/__IBMCPP__ guards and apply to all z/OS compiles
-
- Closes #11241
-
-Daniel Stenberg (2 Jun 2023)
-
-- docs/SECURITY-PROCESS.md: link to example of previous critical flaw
-
-Mark Seuffert (2 Jun 2023)
-
-- README.md: updated link to opencollective
-
- Closes #11232
-
-Daniel Stenberg (1 Jun 2023)
-
-- libssh2: use custom memory functions
-
- 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.
-
- Reverts 8b5f100db388ee60118c08aa28
-
- Ref: https://github.com/libssh2/libssh2/issues/1078
- Closes #11235
-
-- test447: test PUTting a file that grows
-
- ... and have curl trim the end when it reaches the expected total amount
- of bytes instead of over-sending.
-
- Reported-by: JustAnotherArchivist on github
- Closes #11223
-
-- curl: count uploaded data to stop at the originally given size
-
- Closes #11223
- Fixes #11222
- Reported-by: JustAnotherArchivist on github
-
-- tool: remove exclamation marks from error/warning messages
-
-- tool: use errorf() for error output
-
- Convert a number of fprintf() calls.
-
-- tool: remove newlines from all helpf/notef/warnf/errorf calls
-
- Make voutf() always add one.
-
- Closes #11226
-
-- tests/servers.pm: pick unused port number with a server socket
-
- 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.
-
- 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.
-
- Closes #11220
-
-- RELEASE-NOTES: synced
-
- bump to 8.2.0
-
-Alejandro R. Sedeño (31 May 2023)
-
-- configure: fix run-compiler for old /bin/sh
-
- If you try to assign and export on the same line on some older /bin/sh
- implementations, it complains:
-
- ```
- $ export "NAME=value"
- NAME=value: is not an identifier
- ```
-
- 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:
-
- ```
- NAME="value"
- export NAME
- ```
-
- 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 #11228
-
-Philip Heiduck (31 May 2023)
-
-- circleci: install impacket & wolfssl 5.6.0
-
- Closes #11221
-
-Daniel Stenberg (31 May 2023)
-
-- tool_urlglob: use curl_off_t instead of longs
-
- To handle more globs better (especially on Windows)
-
- Closes #11224
-
-Dan Fandrich (30 May 2023)
-
-- scripts: Fix GHA matrix job detection in cijobs.pl
-
- The parsing is pretty brittle and it broke detecting some jobs at some
- point. Also, detect if Windows is used in GHA.
-
-- runtests: abort test run after failure without -a
-
- This was broken in a recent refactor and test runs would not stop.
-
- Follow-up to d4a1b5b6
-
- Reported-by: Daniel Stenberg
- Fixes #11225
- Closes #11227
-
-Version 8.1.2 (30 May 2023)
-
-Daniel Stenberg (30 May 2023)
-
-- RELEASE-NOTES: synced
-
- 8.1.2 release
-
-- THANKS: contributors from 8.1.2
diff --git a/libs/libcurl/docs/COPYING b/libs/libcurl/docs/COPYING
index 5cf733b096..5f6dc0aef2 100644
--- a/libs/libcurl/docs/COPYING
+++ b/libs/libcurl/docs/COPYING
@@ -1,6 +1,6 @@
COPYRIGHT AND PERMISSION NOTICE
-Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2024, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
diff --git a/libs/libcurl/docs/THANKS b/libs/libcurl/docs/THANKS
index d7e05f07ef..0c1d0c7589 100644
--- a/libs/libcurl/docs/THANKS
+++ b/libs/libcurl/docs/THANKS
@@ -92,6 +92,7 @@ Alex Samorukov
Alex Suykov
Alex Vinnik
Alex Xu
+Alexander Bartel
Alexander Beedie
Alexander Chuykov
Alexander Dyagilev
@@ -206,6 +207,7 @@ Andy Stamp
Andy Tsouladze
Angus Mackay
anio on github
+annalee
anon00000000 on github
anshnd on github
Anssi Kolehmainen
@@ -290,6 +292,7 @@ Basuke Suzuki
baumanj on github
bdry on github
beckenc on github
+Ben
Ben Boeckel
Ben Darnell
Ben Fritz
@@ -373,6 +376,7 @@ Brandon Dong
Brandon Wang
BratSinot on github
Brendan Jurd
+Brennan Kinney
Brent Beardsley
Brian Akins
Brian Bergeron
@@ -404,11 +408,13 @@ Bryan Henderson
Bryan Kemp
bsammon on github
bsergean on github
+bubbleguuum on github
Bubu on github
buzo-ffm on github
bxac on github
Bylon2 on github
Byrial Jensen
+Cajus Pollmeier
Caleb Raitto
Calvin Buckley
calvin2021y on github
@@ -441,6 +447,7 @@ Cering on github
Cesar Eduardo Barros
Chad Monroe
Chandrakant Bagul
+Chara White
Charles Cazabon
Charles Kerr
Charles Romestant
@@ -465,6 +472,7 @@ Chris Maltby
Chris Mumford
Chris Paulson-Ellis
Chris Roberts
+Chris Sauer
Chris Smowton
Chris Talbot
Chris Young
@@ -681,6 +689,7 @@ Denis Laxalde
Denis Ollier
Dennis Clarke
Dennis Felsing
+dependabot[bot]
Derek Higgins
Derzsi Dániel
Desmond O. Chang
@@ -943,6 +952,7 @@ FuccDucc on github
Fujii Hironori
fullincome on github
fundawang on github
+Gabe
Gabriel Corona
Gabriel Kuri
Gabriel Simmer
@@ -1055,6 +1065,7 @@ Hannes Magnusson
Hanno Böck
Hanno Kranzhoff
Hans Steegers
+Hans-Christian Egtvedt
Hans-Christian Noren Egtvedt
Hans-Jurgen May
Hao Wu
@@ -1066,6 +1077,7 @@ Harry Sarson
Harry Sintonen
Harshal Pradhan
Hauke Duden
+Haydar Alaidrus
Hayden Roche
He Qin
Heikki Korpela
@@ -1084,6 +1096,7 @@ Henry Ludemann
Henry Roeland
Herve Amblard
HexTheDragon
+hgdagon on github
Hide Ishikawa
Hidemoto Nakada
highmtworks on github
@@ -1117,6 +1130,7 @@ Ian Lynagh
Ian Spence
Ian Turner
Ian Wilkes
+iAroc on github
iconoclasthero
icy17 on github
Ignacio Vazquez-Abrams
@@ -1155,6 +1169,7 @@ Ishan SinghLevett
Ithubg on github
Ivan Avdeev
Ivan Tsybulin
+ivanfywang
IvanoG on github
Ivo Bellin Salarin
iz8mbw on github
@@ -1214,6 +1229,7 @@ Jan Venekamp
Jan Verbeek
Jan-Piet Mens
JanB on github
+janko-js on github
Janne Blomqvist
Janne Johansson
Jared Jennings
@@ -1235,6 +1251,7 @@ Javier Navarro
Javier Sixto
Jay Austin
Jay Dommaschk
+Jay Wu
Jayesh A Shah
Jaz Fresh
JazJas on github
@@ -1289,6 +1306,7 @@ Jerry Krinock
Jerry Wu
Jes Badwal
Jesper Jensen
+Jess Lowe
Jesse Chisholm
Jesse Noller
Jesse Tan
@@ -1600,6 +1618,7 @@ Lawrence Gripper
Lawrence Matthews
Lawrence Wagerfield
Leah Neukirchen
+Lealem Amedie
Leandro Coutinho
Legoff Vincent
Lehel Bernadt
@@ -1623,6 +1642,7 @@ LigH-de on github
lijian996 on github
Lijo Antony
lilongyan-huawei on github
+Lin Sun
Linas Vepstas
Lindley French
Ling Thio
@@ -1739,6 +1759,7 @@ Mark Davies
Mark Dodgson
Mark Gaiser
Mark Hamilton
+Mark Huang
Mark Incley
Mark Itzcovitz
Mark Karpeles
@@ -1747,6 +1768,7 @@ Mark Nottingham
Mark Roszko
Mark Salisbury
Mark Seuffert
+Mark Sinkovics
Mark Snelling
Mark Swaanenburg
Mark Tully
@@ -1835,6 +1857,7 @@ Matthias Naegler
Mattias Fornander
Matus Uzak
Maurice Barnum
+Mauricio Scheffer
Mauro Iorio
Mauro Rappa
Maurício Meneghini Fauth
@@ -1958,6 +1981,7 @@ Mohamed Lrhazi
Mohamed Osama
Mohammad AlSaleh
Mohammad Hasbini
+Mohammadreza Hendiani
Mohammed Naser
Mohun Biswas
momala454 on github
@@ -2109,6 +2133,7 @@ Oskar Liljeblad
Oskar Sigvardsson
Oumph on github
ovidiu-benea on github
+Ozan Cansel
P R Schaffner
Pablo Busse
Palo Markovic
@@ -2261,6 +2286,7 @@ Prithvi MK
privetryan on github
Priyanka Shah
ProceduralMan on github
+promptfuzz_ on hackerone
Pronyushkin Petr
Przemysław Tomaszewski
pszemus on github
@@ -2330,6 +2356,7 @@ Renaud Lehoux
Rene Bernhardt
Rene Rebe
Reuven Wachtfogel
+RevaliQaQ on github
Reza Arbab
Rianov Viacheslav
Ricardo Cadime
@@ -2355,6 +2382,7 @@ Richard Gorton
Richard Gray
Richard Hosking
Richard Hsu
+Richard Levitte
Richard Marion
Richard Michael
Richard Moore
@@ -2511,6 +2539,7 @@ Saul good
Saurav Babu
sayrer on github
SBKarr on github
+Scarlett McAllister
Scott Bailey
Scott Barrett
Scott Cantor
@@ -2556,6 +2585,7 @@ SerusDev on github
Seshubabu Pasam
Seth Mos
Sevan Janiyan
+sfan5 on github
Sgharat on github
Sh Diao
Shachaf Ben-Kiki
@@ -2705,6 +2735,7 @@ Taneli Vähäkangas
Tanguy Fautre
Taras Kushnir
tarek112 on github
+Tatsuhiko Miyagawa
Tatsuhiro Tsujikawa
tawmoto on github
tbugfinder on github
@@ -2717,6 +2748,7 @@ thanhchungbtc on github
The Infinnovation team
TheAssassin on github
TheKnarf on github
+Theo
Theodore Dubois
therealhirudo on github
Thiago Suchorski
@@ -2724,6 +2756,7 @@ tholin on github
Thomas Bouzerar
Thomas Braun
Thomas Danielsson
+Thomas Ferguson
Thomas Gamper
Thomas Glanzmann
Thomas Guillem
@@ -2961,6 +2994,7 @@ x2018 on github
Xavier Bouchoux
XhmikosR on github
XhstormR on github
+Xi Ruoyao
Xiang Xiao
Xiangbin Li
xianghongai on github
@@ -2972,6 +3006,7 @@ xtonik on github
xwxbug on github
Xì Gà
Yaakov Selkowitz
+Yadhu Krishna M
Yair Lenga
Yang Tse
Yaobin Wen
@@ -2979,10 +3014,12 @@ Yarram Sunil
Yasuharu Yamada
Yasuhiro Matsumoto
Yechiel Kalmenson
+Yedaya Katsman
Yehezkel Horowitz
Yehoshua Hershberg
ygthien on github
Yi Huang
+Yifei Kong
Yiming Jing
Yingwei Liu
yiyuaner on github
@@ -3010,6 +3047,8 @@ Zachary Seguin
Zdenek Pavlas
Zekun Ni
zelinchen on github
+zengwei
+zengwei2000
Zenju on github
Zero King
Zespre Schmidt
diff --git a/libs/libcurl/include/curl/curl.h b/libs/libcurl/include/curl/curl.h
index 1f6c5764c6..e69cb4fc17 100644
--- a/libs/libcurl/include/curl/curl.h
+++ b/libs/libcurl/include/curl/curl.h
@@ -631,6 +631,7 @@ typedef enum {
CURLE_PROXY, /* 97 - proxy handshake error */
CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */
CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */
+ CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */
CURL_LAST /* never use! */
} CURLcode;
@@ -1845,7 +1846,8 @@ typedef enum {
/* allow GSSAPI credential delegation */
CURLOPT(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_VALUES, 210),
- /* Set the name servers to use for DNS resolution */
+ /* Set the name servers to use for DNS resolution.
+ * Only supported by the c-ares DNS backend */
CURLOPT(CURLOPT_DNS_SERVERS, CURLOPTTYPE_STRINGPOINT, 211),
/* Time-out accept operations (currently for FTP only) after this amount
@@ -2201,6 +2203,9 @@ typedef enum {
/* set a specific client IP for HAProxy PROXY protocol header? */
CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323),
+ /* millisecond version */
+ CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -2932,7 +2937,8 @@ typedef enum {
CURLINFO_CAPATH = CURLINFO_STRING + 62,
CURLINFO_XFER_ID = CURLINFO_OFF_T + 63,
CURLINFO_CONN_ID = CURLINFO_OFF_T + 64,
- CURLINFO_LASTONE = 64
+ CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65,
+ CURLINFO_LASTONE = 65
} CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
diff --git a/libs/libcurl/include/curl/curlver.h b/libs/libcurl/include/curl/curlver.h
index e54a5e6138..8d3879a30a 100644
--- a/libs/libcurl/include/curl/curlver.h
+++ b/libs/libcurl/include/curl/curlver.h
@@ -32,12 +32,12 @@
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "8.5.0"
+#define LIBCURL_VERSION "8.6.0"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 5
+#define LIBCURL_VERSION_MINOR 6
#define LIBCURL_VERSION_PATCH 0
/* This is the numeric version of the libcurl version number, meant for easier
@@ -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 0x080500
+#define LIBCURL_VERSION_NUM 0x080600
/*
* 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-12-06"
+#define LIBCURL_TIMESTAMP "2024-01-31"
#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
#define CURL_AT_LEAST_VERSION(x,y,z) \
diff --git a/libs/libcurl/include/curl/mprintf.h b/libs/libcurl/include/curl/mprintf.h
index b344c8c830..7880024983 100644
--- a/libs/libcurl/include/curl/mprintf.h
+++ b/libs/libcurl/include/curl/mprintf.h
@@ -34,19 +34,27 @@ extern "C" {
#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)))
+ !defined(CURL_NO_FMT_CHECKS)
+#if defined(__MINGW32__) && !defined(__clang__)
+#define CURL_TEMP_PRINTF(fmt, arg) \
+ __attribute__((format(gnu_printf, fmt, arg)))
#else
-#define CURL_TEMP_PRINTF(a,b)
+#define CURL_TEMP_PRINTF(fmt, arg) \
+ __attribute__((format(printf, fmt, arg)))
+#endif
+#else
+#define CURL_TEMP_PRINTF(fmt, arg)
#endif
-CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2);
+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_TEMP_PRINTF(3, 4);
+ 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)
diff --git a/libs/libcurl/include/curl/system.h b/libs/libcurl/include/curl/system.h
index 40044a4ee3..01ff338b15 100644
--- a/libs/libcurl/include/curl/system.h
+++ b/libs/libcurl/include/curl/system.h
@@ -184,9 +184,8 @@
# define CURL_FORMAT_CURL_OFF_TU PRIu64
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
-# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_PULL_SYS_TYPES_H 1
-# define CURL_PULL_WS2TCPIP_H 1
#elif defined(__VMS)
# if defined(__VAX)
@@ -417,15 +416,6 @@
#define CURL_PULL_SYS_POLL_H
#endif
-
-/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file */
-/* ws2tcpip.h is required here to properly make type definitions below. */
-#ifdef CURL_PULL_WS2TCPIP_H
-# include <winsock2.h>
-# include <windows.h>
-# include <ws2tcpip.h>
-#endif
-
/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */
/* sys/types.h is required here to properly make type definitions below. */
#ifdef CURL_PULL_SYS_TYPES_H
diff --git a/libs/libcurl/include/curl/urlapi.h b/libs/libcurl/include/curl/urlapi.h
index 1c9b11fec5..f5f2a2b771 100644
--- a/libs/libcurl/include/curl/urlapi.h
+++ b/libs/libcurl/include/curl/urlapi.h
@@ -63,6 +63,7 @@ typedef enum {
CURLUE_BAD_SLASHES, /* 28 */
CURLUE_BAD_USER, /* 29 */
CURLUE_LACKS_IDN, /* 30 */
+ CURLUE_TOO_LARGE, /* 31 */
CURLUE_LAST
} CURLUcode;
diff --git a/libs/libcurl/src/Makefile.in b/libs/libcurl/src/Makefile.in
index 3beb50dc1f..662f586563 100644
--- a/libs/libcurl/src/Makefile.in
+++ b/libs/libcurl/src/Makefile.in
@@ -244,40 +244,42 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
vtls/openssl.c vtls/rustls.c vtls/schannel.c \
vtls/schannel_verify.c vtls/sectransp.c vtls/vtls.c \
vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
- vquic/curl_ngtcp2.c vquic/curl_quiche.c vquic/vquic.c \
- vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c altsvc.h amigaos.h \
- arpa_telnet.h asyn.h bufq.h bufref.h c-hyper.h cf-h1-proxy.h \
- cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \
- cfilters.h conncache.h connect.h content_encoding.h cookie.h \
- curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \
- curl_endian.h curl_fnmatch.h curl_get_line.h \
- curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \
- curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \
- curl_multibyte.h curl_ntlm_core.h curl_ntlm_wb.h curl_path.h \
- curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h \
- curl_setup.h curl_setup_once.h curl_sha256.h curl_sspi.h \
- curl_threads.h curl_trc.h curlx.h dict.h doh.h dynbuf.h \
- dynhds.h easy_lock.h easyif.h easyoptions.h escape.h file.h \
- fileinfo.h fopen.h formdata.h ftp.h ftplistparser.h \
- functypes.h getinfo.h gopher.h hash.h headers.h hostip.h \
- hsts.h http.h http1.h http2.h http_aws_sigv4.h http_chunks.h \
- http_digest.h http_negotiate.h http_ntlm.h http_proxy.h idn.h \
- if2ip.h imap.h inet_ntop.h inet_pton.h llist.h macos.h \
- memdebug.h mime.h mqtt.h multihandle.h multiif.h netrc.h \
- nonblock.h noproxy.h parsedate.h pingpong.h pop3.h progress.h \
- psl.h rand.h rename.h rtsp.h select.h sendf.h setopt.h \
- setup-vms.h share.h sigpipe.h slist.h smb.h smtp.h sockaddr.h \
- socketpair.h socks.h speedcheck.h splay.h strcase.h strdup.h \
- strerror.h strtok.h strtoofft.h system_win32.h telnet.h tftp.h \
- timediff.h timeval.h transfer.h url.h urlapi-int.h urldata.h \
+ vquic/curl_ngtcp2.c vquic/curl_osslq.c vquic/curl_quiche.c \
+ vquic/vquic.c vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c \
+ vssh/wolfssh.c altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h \
+ bufref.h c-hyper.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \
+ cf-https-connect.h cf-socket.h cfilters.h conncache.h \
+ connect.h content_encoding.h cookie.h curl_addrinfo.h \
+ curl_base64.h curl_ctype.h curl_des.h curl_endian.h \
+ curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+ curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+ curl_md5.h curl_memory.h curl_memrchr.h curl_multibyte.h \
+ curl_ntlm_core.h curl_ntlm_wb.h curl_path.h curl_printf.h \
+ curl_range.h curl_rtmp.h curl_sasl.h curl_setup.h \
+ curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h \
+ curl_trc.h curlx.h dict.h doh.h dynbuf.h dynhds.h easy_lock.h \
+ easyif.h easyoptions.h escape.h file.h fileinfo.h fopen.h \
+ formdata.h ftp.h ftplistparser.h functypes.h getinfo.h \
+ gopher.h hash.h headers.h hostip.h hsts.h http.h http1.h \
+ http2.h http_aws_sigv4.h http_chunks.h http_digest.h \
+ http_negotiate.h http_ntlm.h http_proxy.h idn.h if2ip.h imap.h \
+ inet_ntop.h inet_pton.h llist.h macos.h memdebug.h mime.h \
+ mqtt.h multihandle.h multiif.h netrc.h nonblock.h noproxy.h \
+ parsedate.h pingpong.h pop3.h progress.h psl.h rand.h rename.h \
+ rtsp.h select.h sendf.h setopt.h setup-vms.h share.h sigpipe.h \
+ slist.h smb.h smtp.h sockaddr.h socketpair.h socks.h \
+ speedcheck.h splay.h strcase.h strdup.h strerror.h strtok.h \
+ strtoofft.h system_win32.h telnet.h tftp.h timediff.h \
+ timeval.h transfer.h url.h urlapi-int.h urldata.h \
version_win32.h warnless.h ws.h vauth/digest.h vauth/ntlm.h \
vauth/vauth.h vtls/bearssl.h vtls/gtls.h vtls/hostcheck.h \
vtls/keylog.h vtls/mbedtls.h vtls/mbedtls_threadlock.h \
vtls/openssl.h vtls/rustls.h vtls/schannel.h \
vtls/schannel_int.h vtls/sectransp.h vtls/vtls.h \
vtls/vtls_int.h vtls/wolfssl.h vtls/x509asn1.h \
- vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_quiche.h \
- vquic/vquic.h vquic/vquic_int.h vssh/ssh.h libcurl.rc
+ vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_osslq.h \
+ vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \
+ vquic/vquic-tls.h vssh/ssh.h libcurl.rc
am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \
libcurl_la-base64.lo libcurl_la-bufq.lo libcurl_la-bufref.lo \
@@ -351,8 +353,9 @@ am__objects_3 = vtls/libcurl_la-bearssl.lo vtls/libcurl_la-gtls.lo \
vtls/libcurl_la-sectransp.lo vtls/libcurl_la-vtls.lo \
vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo
am__objects_4 = vquic/libcurl_la-curl_msh3.lo \
- vquic/libcurl_la-curl_ngtcp2.lo \
- vquic/libcurl_la-curl_quiche.lo vquic/libcurl_la-vquic.lo
+ vquic/libcurl_la-curl_ngtcp2.lo vquic/libcurl_la-curl_osslq.lo \
+ vquic/libcurl_la-curl_quiche.lo vquic/libcurl_la-vquic.lo \
+ vquic/libcurl_la-vquic-tls.lo
am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \
vssh/libcurl_la-wolfssh.lo
am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
@@ -453,7 +456,9 @@ am__objects_13 = vtls/libcurlu_la-bearssl.lo vtls/libcurlu_la-gtls.lo \
vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo
am__objects_14 = vquic/libcurlu_la-curl_msh3.lo \
vquic/libcurlu_la-curl_ngtcp2.lo \
- vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo
+ vquic/libcurlu_la-curl_osslq.lo \
+ vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo \
+ vquic/libcurlu_la-vquic-tls.lo
am__objects_15 = vssh/libcurlu_la-libssh.lo \
vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-wolfssh.lo
am__objects_16 = $(am__objects_11) $(am__objects_12) $(am__objects_13) \
@@ -763,11 +768,15 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
vauth/$(DEPDIR)/libcurlu_la-vauth.Plo \
vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo \
vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo \
+ vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo \
vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo \
+ vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo \
vquic/$(DEPDIR)/libcurl_la-vquic.Plo \
vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo \
vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo \
vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo \
vquic/$(DEPDIR)/libcurlu_la-vquic.Plo \
vssh/$(DEPDIR)/libcurl_la-libssh.Plo \
vssh/$(DEPDIR)/libcurl_la-libssh2.Plo \
@@ -926,6 +935,7 @@ HAVE_BROTLI = @HAVE_BROTLI@
HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@
HAVE_LDAP_SSL = @HAVE_LDAP_SSL@
HAVE_LIBZ = @HAVE_LIBZ@
+HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@
HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@
HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@
HAVE_ZSTD = @HAVE_ZSTD@
@@ -1003,10 +1013,14 @@ USE_MSH3 = @USE_MSH3@
USE_NGHTTP2 = @USE_NGHTTP2@
USE_NGHTTP3 = @USE_NGHTTP3@
USE_NGTCP2 = @USE_NGTCP2@
+USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@
USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@
USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@
USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@
+USE_NGTCP2_H3 = @USE_NGTCP2_H3@
USE_OPENLDAP = @USE_OPENLDAP@
+USE_OPENSSL_H3 = @USE_OPENSSL_H3@
+USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@
USE_QUICHE = @USE_QUICHE@
USE_RUSTLS = @USE_RUSTLS@
USE_SCHANNEL = @USE_SCHANNEL@
@@ -1186,15 +1200,19 @@ LIB_VTLS_HFILES = \
LIB_VQUIC_CFILES = \
vquic/curl_msh3.c \
vquic/curl_ngtcp2.c \
+ vquic/curl_osslq.c \
vquic/curl_quiche.c \
- vquic/vquic.c
+ vquic/vquic.c \
+ vquic/vquic-tls.c
LIB_VQUIC_HFILES = \
vquic/curl_msh3.h \
vquic/curl_ngtcp2.h \
+ vquic/curl_osslq.h \
vquic/curl_quiche.h \
vquic/vquic.h \
- vquic/vquic_int.h
+ vquic/vquic_int.h \
+ vquic/vquic-tls.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
@@ -1675,10 +1693,14 @@ vquic/libcurl_la-curl_msh3.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurl_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-curl_osslq.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurl_la-curl_quiche.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurl_la-vquic.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-vquic-tls.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
vssh/$(am__dirstamp):
@$(MKDIR_P) vssh
@: > vssh/$(am__dirstamp)
@@ -1752,10 +1774,14 @@ vquic/libcurlu_la-curl_msh3.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurlu_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-curl_osslq.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurlu_la-curl_quiche.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
vquic/libcurlu_la-vquic.lo: vquic/$(am__dirstamp) \
vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-vquic-tls.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
vssh/libcurlu_la-libssh.lo: vssh/$(am__dirstamp) \
vssh/$(DEPDIR)/$(am__dirstamp)
vssh/libcurlu_la-libssh2.lo: vssh/$(am__dirstamp) \
@@ -2068,11 +2094,15 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-vauth.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh2.Plo@am__quote@ # am--include-marker
@@ -3252,6 +3282,13 @@ vquic/libcurl_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c
+vquic/libcurl_la-curl_osslq.lo: vquic/curl_osslq.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_osslq.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_osslq.Tpo -c -o vquic/libcurl_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_osslq.Tpo vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_osslq.c' object='vquic/libcurl_la-curl_osslq.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c
+
vquic/libcurl_la-curl_quiche.lo: vquic/curl_quiche.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-curl_quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_quiche.Tpo -c -o vquic/libcurl_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_quiche.Tpo vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo
@@ -3266,6 +3303,13 @@ vquic/libcurl_la-vquic.lo: vquic/vquic.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+vquic/libcurl_la-vquic-tls.lo: vquic/vquic-tls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-vquic-tls.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic-tls.Tpo -c -o vquic/libcurl_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-vquic-tls.Tpo vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic-tls.c' object='vquic/libcurl_la-vquic-tls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c
+
vssh/libcurl_la-libssh.lo: vssh/libssh.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh.Tpo -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh.Tpo vssh/$(DEPDIR)/libcurl_la-libssh.Plo
@@ -4400,6 +4444,13 @@ vquic/libcurlu_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c
+vquic/libcurlu_la-curl_osslq.lo: vquic/curl_osslq.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_osslq.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Tpo -c -o vquic/libcurlu_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_osslq.c' object='vquic/libcurlu_la-curl_osslq.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_osslq.lo `test -f 'vquic/curl_osslq.c' || echo '$(srcdir)/'`vquic/curl_osslq.c
+
vquic/libcurlu_la-curl_quiche.lo: vquic/curl_quiche.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Tpo -c -o vquic/libcurlu_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo
@@ -4414,6 +4465,13 @@ vquic/libcurlu_la-vquic.lo: vquic/vquic.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+vquic/libcurlu_la-vquic-tls.lo: vquic/vquic-tls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic-tls.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Tpo -c -o vquic/libcurlu_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Tpo vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic-tls.c' object='vquic/libcurlu_la-vquic-tls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic-tls.lo `test -f 'vquic/vquic-tls.c' || echo '$(srcdir)/'`vquic/vquic-tls.c
+
vssh/libcurlu_la-libssh.lo: vssh/libssh.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh.Plo
@@ -4869,11 +4927,15 @@ distclean: distclean-am
-rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo
-rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo
-rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo
@@ -5242,11 +5304,15 @@ maintainer-clean: maintainer-clean-am
-rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_msh3.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-curl_osslq.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo
-rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_msh3.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_osslq.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo
-rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo
-rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo
-rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo
diff --git a/libs/libcurl/src/Makefile.inc b/libs/libcurl/src/Makefile.inc
index efaf2f50db..a607bee30c 100644
--- a/libs/libcurl/src/Makefile.inc
+++ b/libs/libcurl/src/Makefile.inc
@@ -78,15 +78,19 @@ LIB_VTLS_HFILES = \
LIB_VQUIC_CFILES = \
vquic/curl_msh3.c \
vquic/curl_ngtcp2.c \
+ vquic/curl_osslq.c \
vquic/curl_quiche.c \
- vquic/vquic.c
+ vquic/vquic.c \
+ vquic/vquic-tls.c
LIB_VQUIC_HFILES = \
vquic/curl_msh3.h \
vquic/curl_ngtcp2.h \
+ vquic/curl_osslq.h \
vquic/curl_quiche.h \
vquic/vquic.h \
- vquic/vquic_int.h
+ vquic/vquic_int.h \
+ vquic/vquic-tls.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c
index 13d4f11b71..f410c2e7b2 100644
--- a/libs/libcurl/src/altsvc.c
+++ b/libs/libcurl/src/altsvc.c
@@ -106,9 +106,11 @@ static struct altsvc *altsvc_createid(const char *srchost,
dlen = strlen(dsthost);
DEBUGASSERT(hlen);
DEBUGASSERT(dlen);
- if(!hlen || !dlen)
+ if(!hlen || !dlen) {
/* bad input */
+ free(as);
return NULL;
+ }
if((hlen > 2) && srchost[0] == '[') {
/* IPv6 address, strip off brackets */
srchost++;
@@ -123,11 +125,11 @@ static struct altsvc *altsvc_createid(const char *srchost,
dlen -= 2;
}
- as->src.host = Curl_strndup(srchost, hlen);
+ as->src.host = Curl_memdup0(srchost, hlen);
if(!as->src.host)
goto error;
- as->dst.host = Curl_strndup(dsthost, dlen);
+ as->dst.host = Curl_memdup0(dsthost, dlen);
if(!as->dst.host)
goto error;
@@ -333,9 +335,6 @@ CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
{
DEBUGASSERT(asi);
- if(!ctrl)
- /* unexpected */
- return CURLE_BAD_FUNCTION_ARGUMENT;
asi->flags = ctrl;
return CURLE_OK;
}
diff --git a/libs/libcurl/src/asyn-ares.c b/libs/libcurl/src/asyn-ares.c
index f13ccb36cb..69d22ea9fe 100644
--- a/libs/libcurl/src/asyn-ares.c
+++ b/libs/libcurl/src/asyn-ares.c
@@ -173,10 +173,26 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
int status;
struct ares_options options;
int optmask = ARES_OPT_SOCK_STATE_CB;
+ static int ares_ver = 0;
options.sock_state_cb = sock_state_cb;
options.sock_state_cb_data = easy;
- options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
- optmask |= ARES_OPT_TIMEOUTMS;
+ if(ares_ver == 0)
+ ares_version(&ares_ver);
+
+ if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */
+ options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
+ optmask |= ARES_OPT_TIMEOUTMS;
+ }
+
+ /*
+ if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
+
+ if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
+ to set the timeout value;
+
+ if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
+ overwrite c-ares' timeout.
+ */
status = ares_init_options((ares_channel*)resolver, &options, optmask);
if(status != ARES_SUCCESS) {
@@ -228,9 +244,9 @@ static void destroy_async_data(struct Curl_async *async);
void Curl_resolver_cancel(struct Curl_easy *data)
{
DEBUGASSERT(data);
- if(data->conn->resolve_async.resolver)
- ares_cancel((ares_channel)data->conn->resolve_async.resolver);
- destroy_async_data(&data->conn->resolve_async);
+ if(data->state.async.resolver)
+ ares_cancel((ares_channel)data->state.async.resolver);
+ destroy_async_data(&data->state.async);
}
/*
@@ -278,14 +294,14 @@ int Curl_resolver_getsock(struct Curl_easy *data,
struct timeval timebuf;
struct timeval *timeout;
long milli;
- int max = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
+ int max = ares_getsock((ares_channel)data->state.async.resolver,
(ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
maxtime.tv_usec = 0;
- timeout = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
- &maxtime, &timebuf);
+ timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
+ &timebuf);
milli = (long)curlx_tvtoms(timeout);
if(milli == 0)
milli += 10;
@@ -313,8 +329,8 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
int i;
int num = 0;
- bitmask = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
- socks, ARES_GETSOCK_MAXNUM);
+ bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
+ ARES_GETSOCK_MAXNUM);
for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
pfd[i].events = 0;
@@ -344,12 +360,12 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
if(!nfds)
/* Call ares_process() unconditionally here, even if we simply timed out
above, as otherwise the ares name resolve won't timeout! */
- ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
- ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+ ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
+ ARES_SOCKET_BAD);
else {
/* move through the descriptors and ask for processing on them */
for(i = 0; i < num; i++)
- ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
+ ares_process_fd((ares_channel)data->state.async.resolver,
(pfd[i].revents & (POLLRDNORM|POLLIN))?
pfd[i].fd:ARES_SOCKET_BAD,
(pfd[i].revents & (POLLWRNORM|POLLOUT))?
@@ -368,7 +384,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns)
{
- struct thread_data *res = data->conn->resolve_async.tdata;
+ struct thread_data *res = data->state.async.tdata;
CURLcode result = CURLE_OK;
DEBUGASSERT(dns);
@@ -397,7 +413,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
ARES_ECANCELLED synchronously for all pending responses. This will
leave us with res->num_pending == 0, which is perfect for the next
block. */
- ares_cancel((ares_channel)data->conn->resolve_async.resolver);
+ ares_cancel((ares_channel)data->state.async.resolver);
DEBUGASSERT(res->num_pending == 0);
}
#endif
@@ -408,12 +424,12 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
them */
res->temp_ai = NULL;
- if(!data->conn->resolve_async.dns)
+ if(!data->state.async.dns)
result = Curl_resolver_error(data);
else
- *dns = data->conn->resolve_async.dns;
+ *dns = data->state.async.dns;
- destroy_async_data(&data->conn->resolve_async);
+ destroy_async_data(&data->state.async);
}
return result;
@@ -464,8 +480,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
store.tv_sec = itimeout/1000;
store.tv_usec = (itimeout%1000)*1000;
- tvp = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
- &store, &tv);
+ tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
/* use the timeout period ares returned to us above if less than one
second is left, otherwise just use 1000ms to make sure the progress
@@ -479,7 +494,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
return CURLE_UNRECOVERABLE_POLL;
result = Curl_resolver_is_resolved(data, entry);
- if(result || data->conn->resolve_async.done)
+ if(result || data->state.async.done)
break;
if(Curl_pgrsUpdate(data))
@@ -500,12 +515,12 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
}
if(result)
/* failure, so we cancel the ares operation */
- ares_cancel((ares_channel)data->conn->resolve_async.resolver);
+ ares_cancel((ares_channel)data->state.async.resolver);
/* Operation complete, if the lookup was successful we now have the entry
in the cache. */
if(entry)
- *entry = data->conn->resolve_async.dns;
+ *entry = data->state.async.dns;
if(result)
/* close the connection, since we can't return failure here without
@@ -572,13 +587,12 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */
be valid so only defer it when we know the 'status' says its fine! */
return;
- res = data->conn->resolve_async.tdata;
+ res = data->state.async.tdata;
if(res) {
res->num_pending--;
if(CURL_ASYNC_SUCCESS == status) {
- struct Curl_addrinfo *ai = Curl_he2ai(hostent,
- data->conn->resolve_async.port);
+ struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
if(ai) {
compound_results(res, ai);
}
@@ -729,16 +743,14 @@ static void addrinfo_cb(void *arg, int status, int timeouts,
struct ares_addrinfo *result)
{
struct Curl_easy *data = (struct Curl_easy *)arg;
- if(data->conn) {
- struct thread_data *res = data->conn->resolve_async.tdata;
- (void)timeouts;
- if(ARES_SUCCESS == status) {
- res->temp_ai = ares2addr(result->nodes);
- res->last_status = CURL_ASYNC_SUCCESS;
- ares_freeaddrinfo(result);
- }
- res->num_pending--;
+ struct thread_data *res = data->state.async.tdata;
+ (void)timeouts;
+ if(ARES_SUCCESS == status) {
+ res->temp_ai = ares2addr(result->nodes);
+ res->last_status = CURL_ASYNC_SUCCESS;
+ ares_freeaddrinfo(result);
}
+ res->num_pending--;
}
#endif
@@ -762,12 +774,12 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
res = calloc(1, sizeof(struct thread_data) + namelen);
if(res) {
strcpy(res->hostname, hostname);
- data->conn->resolve_async.hostname = res->hostname;
- data->conn->resolve_async.port = port;
- data->conn->resolve_async.done = FALSE; /* not done */
- data->conn->resolve_async.status = 0; /* clear */
- data->conn->resolve_async.dns = NULL; /* clear */
- data->conn->resolve_async.tdata = res;
+ data->state.async.hostname = res->hostname;
+ data->state.async.port = port;
+ data->state.async.done = FALSE; /* not done */
+ data->state.async.status = 0; /* clear */
+ data->state.async.dns = NULL; /* clear */
+ data->state.async.tdata = res;
/* initial status - failed */
res->last_status = ARES_ENOTFOUND;
@@ -797,8 +809,8 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
hints.ai_flags = ARES_AI_NUMERICSERV;
msnprintf(service, sizeof(service), "%d", port);
res->num_pending = 1;
- ares_getaddrinfo((ares_channel)data->conn->resolve_async.resolver,
- hostname, service, &hints, addrinfo_cb, data);
+ ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
+ service, &hints, addrinfo_cb, data);
}
#else
@@ -808,10 +820,10 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
res->num_pending = 2;
/* areschannel is already setup in the Curl_open() function */
- ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
- hostname, PF_INET, query_completed_cb, data);
- ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
- hostname, PF_INET6, query_completed_cb, data);
+ ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+ PF_INET, query_completed_cb, data);
+ ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+ PF_INET6, query_completed_cb, data);
}
else
#endif
@@ -819,7 +831,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
res->num_pending = 1;
/* areschannel is already setup in the Curl_open() function */
- ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
+ ares_gethostbyname((ares_channel)data->state.async.resolver,
hostname, PF_INET,
query_completed_cb, data);
}
@@ -833,7 +845,6 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
char *servers)
{
CURLcode result = CURLE_NOT_BUILT_IN;
- ares_channel channel, lchannel = NULL;
int ares_result;
/* If server is NULL or empty, this would purge all DNS servers
@@ -846,23 +857,11 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
return CURLE_OK;
#ifdef HAVE_CARES_SERVERS_CSV
- if(data->conn)
- channel = data->conn->resolve_async.resolver;
- else {
- /* we are called by setopt on a data without a connection (yet). In that
- * case we set the value on a local instance for checking.
- * The configured data options are set when the connection for this
- * transfer is created. */
- result = Curl_resolver_init(data, (void **)&lchannel);
- if(result)
- goto out;
- channel = lchannel;
- }
-
#ifdef HAVE_CARES_PORTS_CSV
- ares_result = ares_set_servers_ports_csv(channel, servers);
+ ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
+ servers);
#else
- ares_result = ares_set_servers_csv(channel, servers);
+ ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
#endif
switch(ares_result) {
case ARES_SUCCESS:
@@ -875,12 +874,10 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
case ARES_ENODATA:
case ARES_EBADSTR:
default:
+ DEBUGF(infof(data, "bad servers set"));
result = CURLE_BAD_FUNCTION_ARGUMENT;
break;
}
-out:
- if(lchannel)
- Curl_resolver_cleanup(lchannel);
#else /* too old c-ares version! */
(void)data;
(void)(ares_result);
@@ -892,14 +889,11 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
const char *interf)
{
#ifdef HAVE_CARES_LOCAL_DEV
- if(data->conn) {
- /* not a setopt test run, set the value */
- if(!interf)
- interf = "";
+ if(!interf)
+ interf = "";
+
+ ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
- ares_set_local_dev((ares_channel)data->conn->resolve_async.resolver,
- interf);
- }
return CURLE_OK;
#else /* c-ares version too old! */
(void)data;
@@ -919,15 +913,13 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
}
else {
if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
+ DEBUGF(infof(data, "bad DNS IPv4 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
- if(data->conn) {
- /* not a setopt test run, set the value */
- ares_set_local_ip4((ares_channel)data->conn->resolve_async.resolver,
- ntohl(a4.s_addr));
- }
+ ares_set_local_ip4((ares_channel)data->state.async.resolver,
+ ntohl(a4.s_addr));
return CURLE_OK;
#else /* c-ares version too old! */
@@ -949,14 +941,12 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
}
else {
if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
+ DEBUGF(infof(data, "bad DNS IPv6 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
- if(data->conn) {
- /* not a setopt test run, set the value */
- ares_set_local_ip6((ares_channel)data->conn->resolve_async.resolver, a6);
- }
+ ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
return CURLE_OK;
#else /* c-ares version too old! */
diff --git a/libs/libcurl/src/asyn-thread.c b/libs/libcurl/src/asyn-thread.c
index 493f009e34..c0ee113098 100644
--- a/libs/libcurl/src/asyn-thread.c
+++ b/libs/libcurl/src/asyn-thread.c
@@ -54,6 +54,7 @@
# define RESOLVER_ENOMEM ENOMEM
#endif
+#include "system_win32.h"
#include "urldata.h"
#include "sendf.h"
#include "hostip.h"
@@ -136,7 +137,7 @@ static void destroy_async_data(struct Curl_async *);
*/
void Curl_resolver_cancel(struct Curl_easy *data)
{
- destroy_async_data(&data->conn->resolve_async);
+ destroy_async_data(&data->state.async);
}
/* This function is used to init a threaded resolve */
@@ -144,9 +145,22 @@ static bool init_resolve_thread(struct Curl_easy *data,
const char *hostname, int port,
const struct addrinfo *hints);
+#ifdef _WIN32
+/* Thread sync data used by GetAddrInfoExW for win8+ */
+struct thread_sync_data_w8
+{
+ OVERLAPPED overlapped;
+ ADDRINFOEXW_ *res;
+ HANDLE cancel_ev;
+ ADDRINFOEXW_ hints;
+};
+#endif
/* Data for synchronization between resolver thread and its parent */
struct thread_sync_data {
+#ifdef _WIN32
+ struct thread_sync_data_w8 w8;
+#endif
curl_mutex_t *mtx;
int done;
int port;
@@ -165,6 +179,9 @@ struct thread_sync_data {
};
struct thread_data {
+#ifdef _WIN32
+ HANDLE complete_ev;
+#endif
curl_thread_t thread_hnd;
unsigned int poll_interval;
timediff_t interval_end;
@@ -173,7 +190,7 @@ struct thread_data {
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
{
- return &(data->conn->resolve_async.tdata->tsd);
+ return &(data->state.async.tdata->tsd);
}
/* Destroy resolver thread synchronization data */
@@ -276,6 +293,151 @@ static CURLcode getaddrinfo_complete(struct Curl_easy *data)
return result;
}
+#ifdef _WIN32
+static VOID WINAPI
+query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
+{
+ size_t ss_size;
+ const ADDRINFOEXW_ *ai;
+ struct Curl_addrinfo *ca;
+ struct Curl_addrinfo *cafirst = NULL;
+ struct Curl_addrinfo *calast = NULL;
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-align"
+#endif
+ struct thread_sync_data *tsd =
+ CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ struct thread_data *td = tsd->td;
+ const ADDRINFOEXW_ *res = tsd->w8.res;
+ int error = (int)err;
+ (void)bytes;
+
+ if(error == ERROR_SUCCESS) {
+ /* traverse the addrinfo list */
+
+ for(ai = res; ai != NULL; ai = ai->ai_next) {
+ size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
+ /* ignore elements with unsupported address family, */
+ /* settle family-specific sockaddr structure size. */
+ if(ai->ai_family == AF_INET)
+ ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+ else if(ai->ai_family == AF_INET6)
+ ss_size = sizeof(struct sockaddr_in6);
+#endif
+ else
+ continue;
+
+ /* ignore elements without required address info */
+ if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+ continue;
+
+ /* ignore elements with bogus address size */
+ if((size_t)ai->ai_addrlen < ss_size)
+ continue;
+
+ ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
+ if(!ca) {
+ error = EAI_MEMORY;
+ break;
+ }
+
+ /* copy each structure member individually, member ordering, */
+ /* size, or padding might be different for each platform. */
+ ca->ai_flags = ai->ai_flags;
+ ca->ai_family = ai->ai_family;
+ ca->ai_socktype = ai->ai_socktype;
+ ca->ai_protocol = ai->ai_protocol;
+ ca->ai_addrlen = (curl_socklen_t)ss_size;
+ ca->ai_addr = NULL;
+ ca->ai_canonname = NULL;
+ ca->ai_next = NULL;
+
+ ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+ memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+ if(namelen) {
+ size_t i;
+ ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
+ for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
+ ca->ai_canonname[i] = (char)ai->ai_canonname[i];
+ ca->ai_canonname[namelen] = '\0';
+ }
+
+ /* if the return list is empty, this becomes the first element */
+ if(!cafirst)
+ cafirst = ca;
+
+ /* add this element last in the return list */
+ if(calast)
+ calast->ai_next = ca;
+ calast = ca;
+ }
+
+ /* if we failed, also destroy the Curl_addrinfo list */
+ if(error) {
+ Curl_freeaddrinfo(cafirst);
+ cafirst = NULL;
+ }
+ else if(!cafirst) {
+#ifdef EAI_NONAME
+ /* rfc3493 conformant */
+ error = EAI_NONAME;
+#else
+ /* rfc3493 obsoleted */
+ error = EAI_NODATA;
+#endif
+#ifdef USE_WINSOCK
+ SET_SOCKERRNO(error);
+#endif
+ }
+ tsd->res = cafirst;
+ }
+
+ if(tsd->w8.res) {
+ Curl_FreeAddrInfoExW(tsd->w8.res);
+ tsd->w8.res = NULL;
+ }
+
+ if(error) {
+ tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
+ if(tsd->sock_error == 0)
+ tsd->sock_error = RESOLVER_ENOMEM;
+ }
+ else {
+ Curl_addrinfo_set_port(tsd->res, tsd->port);
+ }
+
+ Curl_mutex_acquire(tsd->mtx);
+ if(tsd->done) {
+ /* too late, gotta clean up the mess */
+ Curl_mutex_release(tsd->mtx);
+ destroy_thread_sync_data(tsd);
+ free(td);
+ }
+ else {
+#ifndef CURL_DISABLE_SOCKETPAIR
+ char buf[1];
+ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
+ /* DNS has been resolved, signal client task */
+ buf[0] = 1;
+ if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
+ /* update sock_erro to errno */
+ tsd->sock_error = SOCKERRNO;
+ }
+ }
+#endif
+ tsd->done = 1;
+ Curl_mutex_release(tsd->mtx);
+ if(td->complete_ev)
+ SetEvent(td->complete_ev); /* Notify caller that the query completed */
+ }
+}
+#endif
#ifdef HAVE_GETADDRINFO
@@ -391,9 +553,21 @@ static void destroy_async_data(struct Curl_async *async)
Curl_mutex_release(td->tsd.mtx);
if(!done) {
+#ifdef _WIN32
+ if(td->complete_ev)
+ CloseHandle(td->complete_ev);
+ else
+#endif
Curl_thread_destroy(td->thread_hnd);
}
else {
+#ifdef _WIN32
+ if(td->complete_ev) {
+ Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
+ WaitForSingleObject(td->complete_ev, INFINITE);
+ CloseHandle(td->complete_ev);
+ }
+#endif
if(td->thread_hnd != curl_thread_t_null)
Curl_thread_join(&td->thread_hnd);
@@ -428,9 +602,9 @@ static bool init_resolve_thread(struct Curl_easy *data,
{
struct thread_data *td = calloc(1, sizeof(struct thread_data));
int err = ENOMEM;
- struct Curl_async *asp = &data->conn->resolve_async;
+ struct Curl_async *asp = &data->state.async;
- data->conn->resolve_async.tdata = td;
+ data->state.async.tdata = td;
if(!td)
goto errno_exit;
@@ -439,6 +613,9 @@ static bool init_resolve_thread(struct Curl_easy *data,
asp->status = 0;
asp->dns = NULL;
td->thread_hnd = curl_thread_t_null;
+#ifdef _WIN32
+ td->complete_ev = NULL;
+#endif
if(!init_thread_sync_data(td, hostname, port, hints)) {
asp->tdata = NULL;
@@ -454,6 +631,41 @@ static bool init_resolve_thread(struct Curl_easy *data,
/* The thread will set this to 1 when complete. */
td->tsd.done = 0;
+#ifdef _WIN32
+ if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
+ Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
+#define MAX_NAME_LEN 256 /* max domain name is 253 chars */
+#define MAX_PORT_LEN 8
+ WCHAR namebuf[MAX_NAME_LEN];
+ WCHAR portbuf[MAX_PORT_LEN];
+ /* calculate required length */
+ int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
+ -1, NULL, 0);
+ if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
+ /* do utf8 conversion */
+ w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
+ if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
+ swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
+ td->tsd.w8.hints.ai_family = hints->ai_family;
+ td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
+ td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(!td->complete_ev) {
+ /* failed to start, mark it as done here for proper cleanup. */
+ td->tsd.done = 1;
+ goto err_exit;
+ }
+ err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
+ NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
+ NULL, &td->tsd.w8.overlapped,
+ &query_complete, &td->tsd.w8.cancel_ev);
+ if(err != WSA_IO_PENDING)
+ query_complete(err, 0, &td->tsd.w8.overlapped);
+ return TRUE;
+ }
+ }
+ }
+#endif
+
#ifdef HAVE_GETADDRINFO
td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
#else
@@ -488,11 +700,24 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- td = data->conn->resolve_async.tdata;
+ td = data->state.async.tdata;
DEBUGASSERT(td);
+#ifdef _WIN32
+ DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
+#else
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
+#endif
/* wait for the thread to resolve the name */
+#ifdef _WIN32
+ if(td->complete_ev) {
+ WaitForSingleObject(td->complete_ev, INFINITE);
+ CloseHandle(td->complete_ev);
+ if(entry)
+ result = getaddrinfo_complete(data);
+ }
+ else
+#endif
if(Curl_thread_join(&td->thread_hnd)) {
if(entry)
result = getaddrinfo_complete(data);
@@ -500,18 +725,18 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
else
DEBUGASSERT(0);
- data->conn->resolve_async.done = TRUE;
+ data->state.async.done = TRUE;
if(entry)
- *entry = data->conn->resolve_async.dns;
+ *entry = data->state.async.dns;
- if(!data->conn->resolve_async.dns && report)
+ if(!data->state.async.dns && report)
/* a name was not resolved, report error */
result = Curl_resolver_error(data);
- destroy_async_data(&data->conn->resolve_async);
+ destroy_async_data(&data->state.async);
- if(!data->conn->resolve_async.dns && report)
+ if(!data->state.async.dns && report)
connclose(data->conn, "asynch resolve failed");
return result;
@@ -524,7 +749,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
*/
void Curl_resolver_kill(struct Curl_easy *data)
{
- struct thread_data *td = data->conn->resolve_async.tdata;
+ struct thread_data *td = data->state.async.tdata;
/* If we're still resolving, we must wait for the threads to fully clean up,
unfortunately. Otherwise, we can simply cancel to clean up any resolver
@@ -563,7 +788,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **entry)
{
- struct thread_data *td = data->conn->resolve_async.tdata;
+ struct thread_data *td = data->state.async.tdata;
int done = 0;
DEBUGASSERT(entry);
@@ -581,13 +806,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
if(done) {
getaddrinfo_complete(data);
- if(!data->conn->resolve_async.dns) {
+ if(!data->state.async.dns) {
CURLcode result = Curl_resolver_error(data);
- destroy_async_data(&data->conn->resolve_async);
+ destroy_async_data(&data->state.async);
return result;
}
- destroy_async_data(&data->conn->resolve_async);
- *entry = data->conn->resolve_async.dns;
+ destroy_async_data(&data->state.async);
+ *entry = data->state.async.dns;
}
else {
/* poll for name lookup done with exponential backoff up to 250ms */
@@ -619,9 +844,9 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
int ret_val = 0;
timediff_t milli;
timediff_t ms;
- struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
#ifndef CURL_DISABLE_SOCKETPAIR
- struct thread_data *td = data->conn->resolve_async.tdata;
+ struct thread_data *td = data->state.async.tdata;
#else
(void)socks;
#endif
@@ -662,7 +887,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
int port,
int *waitp)
{
- struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
*waitp = 0; /* default to synchronous response */
@@ -691,7 +916,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
{
struct addrinfo hints;
int pf = PF_INET;
- struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
*waitp = 0; /* default to synchronous response */
diff --git a/libs/libcurl/src/bufref.c b/libs/libcurl/src/bufref.c
index 3b69e5958e..644aebf188 100644
--- a/libs/libcurl/src/bufref.c
+++ b/libs/libcurl/src/bufref.c
@@ -25,6 +25,7 @@
#include "curl_setup.h"
#include "urldata.h"
#include "bufref.h"
+#include "strdup.h"
#include "curl_memory.h"
#include "memdebug.h"
@@ -116,12 +117,9 @@ CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len)
DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
if(ptr) {
- cpy = malloc(len + 1);
+ cpy = Curl_memdup0(ptr, len);
if(!cpy)
return CURLE_OUT_OF_MEMORY;
- if(len)
- memcpy(cpy, ptr, len);
- cpy[len] = '\0';
}
Curl_bufref_set(br, cpy, len, curl_free);
diff --git a/libs/libcurl/src/c-hyper.c b/libs/libcurl/src/c-hyper.c
index efb534ccad..4b479733ab 100644
--- a/libs/libcurl/src/c-hyper.c
+++ b/libs/libcurl/src/c-hyper.c
@@ -148,7 +148,7 @@ static int hyper_each_header(void *userdata,
if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
failf(data, "Too long response header");
- data->state.hresult = CURLE_OUT_OF_MEMORY;
+ data->state.hresult = CURLE_TOO_LARGE;
return HYPER_ITER_BREAK;
}
@@ -325,6 +325,9 @@ static CURLcode empty_header(struct Curl_easy *data)
CURLE_WRITE_ERROR : CURLE_OK;
if(result)
failf(data, "hyperstream: couldn't pass blank header");
+ /* Hyper does chunked decoding itself. If it was added during
+ * response header processing, remove it again. */
+ Curl_cwriter_remove_by_name(data, "chunked");
}
return result;
}
diff --git a/libs/libcurl/src/cf-h1-proxy.c b/libs/libcurl/src/cf-h1-proxy.c
index a49d6869a6..1725c0e509 100644
--- a/libs/libcurl/src/cf-h1-proxy.c
+++ b/libs/libcurl/src/cf-h1-proxy.c
@@ -70,6 +70,7 @@ struct h1_tunnel_state {
struct dynbuf request_data;
size_t nsent;
size_t headerlines;
+ struct Curl_chunker ch;
enum keeponval {
KEEPON_DONE,
KEEPON_CONNECT,
@@ -133,6 +134,7 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
+ Curl_httpchunk_init(data, &ts->ch, TRUE);
*pts = ts;
connkeep(cf->conn, "HTTP proxy CONNECT");
@@ -146,14 +148,6 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
{
if(ts->tunnel_state == new_state)
return;
- /* leaving this one */
- switch(ts->tunnel_state) {
- case H1_TUNNEL_CONNECT:
- data->req.ignorebody = FALSE;
- break;
- default:
- break;
- }
/* entering this one */
switch(new_state) {
case H1_TUNNEL_INIT:
@@ -183,7 +177,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H1_TUNNEL_FAILED:
if(new_state == H1_TUNNEL_FAILED)
CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
@@ -212,6 +206,7 @@ static void tunnel_free(struct Curl_cfilter *cf,
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
Curl_dyn_free(&ts->rcvbuf);
Curl_dyn_free(&ts->request_data);
+ Curl_httpchunk_free(data, &ts->ch);
free(ts);
cf->ctx = NULL;
}
@@ -344,8 +339,8 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf,
STRCONST("chunked"))) {
infof(data, "CONNECT responded chunked");
ts->chunked_encoding = TRUE;
- /* init our chunky engine */
- Curl_httpchunk_init(data);
+ /* reset our chunky engine */
+ Curl_httpchunk_reset(data, &ts->ch, TRUE);
}
}
else if(Curl_compareheader(header,
@@ -373,7 +368,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
struct SingleRequest *k = &data->req;
curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
char *linep;
- size_t perline;
+ size_t line_len;
int error, writetype;
#define SELECT_OK 0
@@ -432,17 +427,17 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
break;
}
}
- else {
+ else if(ts->chunked_encoding) {
/* chunked-encoded body, so we need to do the chunked dance
properly to know when the end of the body is reached */
- CHUNKcode r;
- CURLcode extra;
size_t consumed = 0;
/* now parse the chunked piece of data so that we can
properly tell when the stream ends */
- r = Curl_httpchunk_read(data, &byte, 1, &consumed, &extra);
- if(r == CHUNKE_STOP) {
+ result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
+ if(result)
+ return result;
+ if(Curl_httpchunk_is_done(data, &ts->ch)) {
/* we're done reading chunks! */
infof(data, "chunk reading DONE");
ts->keepon = KEEPON_DONE;
@@ -462,19 +457,19 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
ts->headerlines++;
linep = Curl_dyn_ptr(&ts->rcvbuf);
- perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
+ line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
/* output debug if that is requested */
- Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
+ Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
/* send the header to the callback */
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
- result = Curl_client_write(data, writetype, linep, perline);
+ result = Curl_client_write(data, writetype, linep, line_len);
if(result)
return result;
- result = Curl_bump_headersize(data, perline, TRUE);
+ result = Curl_bump_headersize(data, line_len, TRUE);
if(result)
return result;
@@ -497,29 +492,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
" bytes of response-body", ts->cl);
}
else if(ts->chunked_encoding) {
- CHUNKcode r;
- CURLcode extra;
- size_t consumed = 0;
-
infof(data, "Ignore chunked response-body");
-
- /* We set ignorebody true here since the chunked decoder
- function will acknowledge that. Pay attention so that this is
- cleared again when this function returns! */
- k->ignorebody = TRUE;
-
- if(linep[1] == '\n')
- /* this can only be a LF if the letter at index 0 was a CR */
- linep++;
-
- /* now parse the chunked piece of data so that we can properly
- tell when the stream ends */
- r = Curl_httpchunk_read(data, linep + 1, 1, &consumed, &extra);
- if(r == CHUNKE_STOP) {
- /* we're done reading chunks! */
- infof(data, "chunk reading DONE");
- ts->keepon = KEEPON_DONE;
- }
}
else {
/* without content-length or chunked encoding, we
@@ -752,7 +725,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
}
if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
- data->set.str[STRING_USERAGENT]) {
+ data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
struct dynbuf ua;
Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
@@ -912,7 +885,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
if(result)
goto out;
h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H1_TUNNEL_CONNECT:
/* see that the request is completely sent */
@@ -921,7 +894,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
if(result || !done)
goto out;
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H1_TUNNEL_RECEIVE:
/* read what is there */
@@ -936,7 +909,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
goto out;
/* got it */
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H1_TUNNEL_RESPONSE:
CURL_TRC_CF(data, cf, "CONNECT response");
@@ -1030,6 +1003,14 @@ out:
*done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
if(*done) {
cf->connected = TRUE;
+ /* Restore `data->req` fields that may habe been touched */
+ data->req.header = TRUE; /* assume header */
+ data->req.bytecount = 0;
+ data->req.ignorebody = FALSE;
+ Curl_client_cleanup(data);
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+
tunnel_free(cf, data);
}
return result;
diff --git a/libs/libcurl/src/cf-h2-proxy.c b/libs/libcurl/src/cf-h2-proxy.c
index f69098b704..83915a6e26 100644
--- a/libs/libcurl/src/cf-h2-proxy.c
+++ b/libs/libcurl/src/cf-h2-proxy.c
@@ -155,7 +155,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf,
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H2_TUNNEL_FAILED:
if(new_state == H2_TUNNEL_FAILED)
CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id);
@@ -221,10 +221,10 @@ static void drain_tunnel(struct Curl_cfilter *cf,
bits = CURL_CSELECT_IN;
if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len)
bits |= CURL_CSELECT_OUT;
- if(data->state.dselect_bits != bits) {
- CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
+ if(data->state.select_bits != bits) {
+ CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
tunnel->stream_id, bits);
- data->state.dselect_bits = bits;
+ data->state.select_bits = bits;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
@@ -1033,7 +1033,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
if(result)
goto out;
h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H2_TUNNEL_CONNECT:
/* see that the request is completely sent */
@@ -1052,7 +1052,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
result = CURLE_OK;
goto out;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case H2_TUNNEL_RESPONSE:
DEBUGASSERT(ts->has_final_response);
diff --git a/libs/libcurl/src/cf-haproxy.c b/libs/libcurl/src/cf-haproxy.c
index 6036e028a5..902de36345 100644
--- a/libs/libcurl/src/cf-haproxy.c
+++ b/libs/libcurl/src/cf-haproxy.c
@@ -125,7 +125,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
if(result)
goto out;
ctx->state = HAPROXY_SEND;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case HAPROXY_SEND:
len = Curl_dyn_len(&ctx->data_out);
if(len > 0) {
@@ -141,7 +141,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
}
}
ctx->state = HAPROXY_DONE;
- /* FALLTHROUGH */
+ FALLTHROUGH();
default:
Curl_dyn_free(&ctx->data_out);
break;
diff --git a/libs/libcurl/src/cf-https-connect.c b/libs/libcurl/src/cf-https-connect.c
index f2ae2cbee6..05e6bdf044 100644
--- a/libs/libcurl/src/cf-https-connect.c
+++ b/libs/libcurl/src/cf-https-connect.c
@@ -266,7 +266,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
cf->conn->transport);
ctx->state = CF_HC_CONNECT;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CF_HC_CONNECT:
if(cf_hc_baller_is_active(&ctx->h3_baller)) {
diff --git a/libs/libcurl/src/cf-socket.c b/libs/libcurl/src/cf-socket.c
index 8582fdff64..31b0291579 100644
--- a/libs/libcurl/src/cf-socket.c
+++ b/libs/libcurl/src/cf-socket.c
@@ -137,14 +137,14 @@ static void nosigpipe(struct Curl_easy *data,
#define nosigpipe(x,y) Curl_nop_stmt
#endif
-#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
+#if defined(__DragonFly__) || defined(USE_WINSOCK)
/* DragonFlyBSD and Windows use millisecond units */
#define KEEPALIVE_FACTOR(x) (x *= 1000)
#else
#define KEEPALIVE_FACTOR(x)
#endif
-#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
+#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
struct tcp_keepalive {
@@ -163,7 +163,9 @@ tcpkeepalive(struct Curl_easy *data,
/* only set IDLE and INTVL if setting KEEPALIVE is successful */
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
(void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
+ infof(data, "Failed to set SO_KEEPALIVE on fd "
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ sockfd, SOCKERRNO);
}
else {
#if defined(SIO_KEEPALIVE_VALS)
@@ -178,8 +180,9 @@ tcpkeepalive(struct Curl_easy *data,
vals.keepaliveinterval = optval;
if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
NULL, 0, &dummy, NULL, NULL) != 0) {
- infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
- (int)sockfd, WSAGetLastError());
+ infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ sockfd, SOCKERRNO);
}
#else
#ifdef TCP_KEEPIDLE
@@ -187,7 +190,9 @@ tcpkeepalive(struct Curl_easy *data,
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
(void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
+ infof(data, "Failed to set TCP_KEEPIDLE on fd "
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ sockfd, SOCKERRNO);
}
#elif defined(TCP_KEEPALIVE)
/* Mac OS X style */
@@ -195,7 +200,9 @@ tcpkeepalive(struct Curl_easy *data,
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
(void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
+ infof(data, "Failed to set TCP_KEEPALIVE on fd "
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ sockfd, SOCKERRNO);
}
#endif
#ifdef TCP_KEEPINTVL
@@ -203,7 +210,9 @@ tcpkeepalive(struct Curl_easy *data,
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
(void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
+ infof(data, "Failed to set TCP_KEEPINTVL on fd "
+ "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ sockfd, SOCKERRNO);
}
#endif
#endif
@@ -783,6 +792,7 @@ struct cf_socket_ctx {
#endif
BIT(got_first_byte); /* if first byte was received */
BIT(accepted); /* socket was accepted, not connected */
+ BIT(sock_connected); /* socket is "connected", e.g. in UDP */
BIT(active);
BIT(buffer_recv);
};
@@ -983,20 +993,14 @@ 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...";
- }
- else
-#endif
- ipmsg = " Trying %s:%d...";
- infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+ if(ctx->addr.family == AF_INET6) {
+ set_ipv6_v6only(ctx->sock, 0);
+ infof(data, " Trying [%s]:%d...", ctx->r_ip, ctx->r_port);
}
+ else
#endif
+ infof(data, " Trying %s:%d...", ctx->r_ip, ctx->r_port);
#ifdef ENABLE_IPV6
is_tcp = (ctx->addr.family == AF_INET
@@ -1054,7 +1058,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
/* set socket non-blocking */
(void)curlx_nonblock(ctx->sock, TRUE);
-
+ ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
out:
if(result) {
if(ctx->sock != CURL_SOCKET_BAD) {
@@ -1242,11 +1246,14 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
struct cf_socket_ctx *ctx = cf->ctx;
if(ctx->sock != CURL_SOCKET_BAD) {
- if(!cf->connected)
+ if(!cf->connected) {
Curl_pollset_set_out_only(data, ps, ctx->sock);
- else
+ CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num);
+ }
+ else if(!ctx->active) {
Curl_pollset_add_in(data, ps, ctx->sock);
- CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
+ CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num);
+ }
}
}
@@ -1429,36 +1436,11 @@ out:
static void conn_set_primary_ip(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
-#ifdef HAVE_GETPEERNAME
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 */
-
- 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;
(void)data;
-#endif
+ DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip));
+ memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip));
}
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -1574,7 +1556,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
*when = ctx->first_byte_at;
break;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
default:
*when = ctx->connected_at;
break;
@@ -1648,10 +1630,17 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
/* QUIC needs a connected socket, nonblocking */
DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
+#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
+ (void)rc;
+ /* On macOS OpenSSL QUIC fails on connected sockets.
+ * see: <https://github.com/openssl/openssl/issues/23251> */
+#else
rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
if(-1 == rc) {
return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
}
+ ctx->sock_connected = TRUE;
+#endif
set_local_ip(cf, data);
CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
" connected: [%s:%d] -> [%s:%d]",
diff --git a/libs/libcurl/src/cf-socket.h b/libs/libcurl/src/cf-socket.h
index 805e612bd4..6031506526 100644
--- a/libs/libcurl/src/cf-socket.h
+++ b/libs/libcurl/src/cf-socket.h
@@ -34,23 +34,6 @@ struct Curl_easy;
struct connectdata;
struct Curl_sockaddr_ex;
-#ifndef SIZEOF_CURL_SOCKET_T
-/* configure and cmake check and set the define */
-# ifdef _WIN64
-# define SIZEOF_CURL_SOCKET_T 8
-# else
-/* default guess */
-# define SIZEOF_CURL_SOCKET_T 4
-# endif
-#endif
-
-#if SIZEOF_CURL_SOCKET_T < 8
-# define CURL_FORMAT_SOCKET_T "d"
-#else
-# define CURL_FORMAT_SOCKET_T "qd"
-#endif
-
-
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold any
diff --git a/libs/libcurl/src/cfilters.c b/libs/libcurl/src/cfilters.c
index 72783eb53d..653220ddb7 100644
--- a/libs/libcurl/src/cfilters.c
+++ b/libs/libcurl/src/cfilters.c
@@ -760,25 +760,11 @@ static void ps_add(struct Curl_easy *data, struct easy_pollset *ps,
void Curl_pollset_add_socks(struct Curl_easy *data,
struct easy_pollset *ps,
int (*get_socks_cb)(struct Curl_easy *data,
- struct connectdata *conn,
curl_socket_t *socks))
{
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
int bitmap;
- DEBUGASSERT(data->conn);
- bitmap = get_socks_cb(data, data->conn, socks);
- ps_add(data, ps, bitmap, socks);
-}
-
-void Curl_pollset_add_socks2(struct Curl_easy *data,
- struct easy_pollset *ps,
- int (*get_socks_cb)(struct Curl_easy *data,
- curl_socket_t *socks))
-{
- curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
- int bitmap;
-
bitmap = get_socks_cb(data, socks);
ps_add(data, ps, bitmap, socks);
}
diff --git a/libs/libcurl/src/cfilters.h b/libs/libcurl/src/cfilters.h
index a343ea5fb6..5fc11be79c 100644
--- a/libs/libcurl/src/cfilters.h
+++ b/libs/libcurl/src/cfilters.h
@@ -530,12 +530,7 @@ void Curl_pollset_set(struct Curl_easy *data,
void Curl_pollset_add_socks(struct Curl_easy *data,
struct easy_pollset *ps,
int (*get_socks_cb)(struct Curl_easy *data,
- struct connectdata *conn,
curl_socket_t *socks));
-void Curl_pollset_add_socks2(struct Curl_easy *data,
- struct easy_pollset *ps,
- int (*get_socks_cb)(struct Curl_easy *data,
- curl_socket_t *socks));
/**
* Check if the pollset, as is, wants to read and/or write regarding
diff --git a/libs/libcurl/src/config-os400.h b/libs/libcurl/src/config-os400.h
index 355399a1bc..af97f64947 100644
--- a/libs/libcurl/src/config-os400.h
+++ b/libs/libcurl/src/config-os400.h
@@ -116,12 +116,6 @@
/* Define if you have the GNU gssapi libraries */
#undef HAVE_GSSGNU
-/* Define if you have the Heimdal gssapi libraries */
-#define HAVE_GSSHEIMDAL
-
-/* Define if you have the MIT gssapi libraries */
-#undef HAVE_GSSMIT
-
/* Define if you need the malloc.h header file even with stdlib.h */
/* #define NEED_MALLOC_H 1 */
diff --git a/libs/libcurl/src/config-win32.h b/libs/libcurl/src/config-win32.h
index 388871d84a..2514a09c33 100644
--- a/libs/libcurl/src/config-win32.h
+++ b/libs/libcurl/src/config-win32.h
@@ -97,15 +97,6 @@
#define HAVE_UNISTD_H 1
#endif
-/* Define if you have the <windows.h> header file. */
-#define HAVE_WINDOWS_H 1
-
-/* Define if you have the <winsock2.h> header file. */
-#define HAVE_WINSOCK2_H 1
-
-/* Define if you have the <ws2tcpip.h> header file. */
-#define HAVE_WS2TCPIP_H 1
-
/* Define to 1 if you have the <libgen.h> header file. */
#if defined(__MINGW32__)
#define HAVE_LIBGEN_H 1
@@ -167,10 +158,6 @@
/* Define if you have the socket function. */
#define HAVE_SOCKET 1
-/* Define if libSSH2 is in use */
-#define USE_LIBSSH2 1
-#define HAVE_LIBSSH2_H 1
-
/* Define if you have the strcasecmp function. */
#if defined(__MINGW32__)
#define HAVE_STRCASECMP 1
@@ -297,52 +284,6 @@
#define SIZEOF_CURL_OFF_T 8
/* ---------------------------------------------------------------- */
-/* BSD-style lwIP TCP/IP stack SPECIFIC */
-/* ---------------------------------------------------------------- */
-
-/* Define to use BSD-style lwIP TCP/IP stack. */
-/* #define USE_LWIPSOCK 1 */
-
-#ifdef USE_LWIPSOCK
-# undef USE_WINSOCK
-# undef HAVE_WINSOCK2_H
-# undef HAVE_WS2TCPIP_H
-# undef HAVE_GETHOSTNAME
-# undef LWIP_POSIX_SOCKETS_IO_NAMES
-# undef RECV_TYPE_ARG1
-# undef RECV_TYPE_ARG3
-# undef SEND_TYPE_ARG1
-# undef SEND_TYPE_ARG3
-# define HAVE_GETHOSTBYNAME_R
-# define HAVE_GETHOSTBYNAME_R_6
-# define LWIP_POSIX_SOCKETS_IO_NAMES 0
-# define RECV_TYPE_ARG1 int
-# define RECV_TYPE_ARG3 size_t
-# define SEND_TYPE_ARG1 int
-# define SEND_TYPE_ARG3 size_t
-#endif
-
-/* ---------------------------------------------------------------- */
-/* Watt-32 tcp/ip SPECIFIC */
-/* ---------------------------------------------------------------- */
-
-#ifdef USE_WATT32
- #include <tcp.h>
- #undef byte
- #undef word
- #undef USE_WINSOCK
- #undef HAVE_WINSOCK2_H
- #undef HAVE_WS2TCPIP_H
- #define HAVE_SYS_IOCTL_H
- #define HAVE_SYS_SOCKET_H
- #define HAVE_NETINET_IN_H
- #define HAVE_NETDB_H
- #define HAVE_ARPA_INET_H
- #define SOCKET int
-#endif
-
-
-/* ---------------------------------------------------------------- */
/* COMPILER SPECIFIC */
/* ---------------------------------------------------------------- */
@@ -533,9 +474,6 @@ Vista
#define USE_WIN32_LDAP 1
#endif
-/* if SSL is enabled */
-#define USE_OPENSSL 1
-
/* Define to use the Windows crypto library. */
#if !defined(CURL_WINDOWS_APP)
#define USE_WIN32_CRYPTO
diff --git a/libs/libcurl/src/config-win32ce.h b/libs/libcurl/src/config-win32ce.h
index 70a95d0411..7e913dffbe 100644
--- a/libs/libcurl/src/config-win32ce.h
+++ b/libs/libcurl/src/config-win32ce.h
@@ -85,15 +85,6 @@
#define HAVE_UNISTD_H 1
#endif
-/* Define if you have the <windows.h> header file. */
-#define HAVE_WINDOWS_H 1
-
-/* Define if you have the <winsock2.h> header file. */
-#define HAVE_WINSOCK2_H 1
-
-/* Define if you have the <ws2tcpip.h> header file. */
-#define HAVE_WS2TCPIP_H 1
-
/* ---------------------------------------------------------------- */
/* OTHER HEADER INFO */
/* ---------------------------------------------------------------- */
diff --git a/libs/libcurl/src/connect.c b/libs/libcurl/src/connect.c
index 4e77d275e5..eccdc4a7db 100644
--- a/libs/libcurl/src/connect.c
+++ b/libs/libcurl/src/connect.c
@@ -93,25 +93,17 @@
* transfer/connection. If the value is 0, there's no timeout (ie there's
* infinite time left). If the value is negative, the timeout time has already
* elapsed.
- *
- * If 'nowp' is non-NULL, it points to the current time.
- * 'duringconnect' is FALSE if not during a connect, as then of course the
- * connect timeout is not taken into account!
- *
+ * @param data the transfer to check on
+ * @param nowp timestamp to use for calculdation, NULL to use Curl_now()
+ * @param duringconnect TRUE iff connect timeout is also taken into account.
* @unittest: 1303
*/
-
-#define TIMEOUT_CONNECT 1
-#define TIMEOUT_MAXTIME 2
-
timediff_t Curl_timeleft(struct Curl_easy *data,
struct curltime *nowp,
bool duringconnect)
{
- unsigned int timeout_set = 0;
- timediff_t connect_timeout_ms = 0;
- timediff_t maxtime_timeout_ms = 0;
- timediff_t timeout_ms = 0;
+ timediff_t timeleft_ms = 0;
+ timediff_t ctimeleft_ms = 0;
struct curltime now;
/* The duration of a connect and the total transfer are calculated from two
@@ -119,43 +111,35 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
before the connect timeout expires and we must acknowledge whichever
timeout that is reached first. The total timeout is set per entire
operation, while the connect timeout is set per connect. */
-
- if(data->set.timeout > 0) {
- timeout_set = TIMEOUT_MAXTIME;
- maxtime_timeout_ms = data->set.timeout;
- }
- if(duringconnect) {
- timeout_set |= TIMEOUT_CONNECT;
- connect_timeout_ms = (data->set.connecttimeout > 0) ?
- data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
- }
- if(!timeout_set)
- /* no timeout */
- return 0;
+ if(data->set.timeout <= 0 && !duringconnect)
+ return 0; /* no timeout in place or checked, return "no limit" */
if(!nowp) {
now = Curl_now();
nowp = &now;
}
- if(timeout_set & TIMEOUT_MAXTIME) {
- maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
- timeout_ms = maxtime_timeout_ms;
+ if(data->set.timeout > 0) {
+ timeleft_ms = data->set.timeout -
+ Curl_timediff(*nowp, data->progress.t_startop);
+ if(!timeleft_ms)
+ timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
+ if(!duringconnect)
+ return timeleft_ms; /* no connect check, this is it */
}
- if(timeout_set & TIMEOUT_CONNECT) {
- connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
-
- if(!(timeout_set & TIMEOUT_MAXTIME) ||
- (connect_timeout_ms < maxtime_timeout_ms))
- timeout_ms = connect_timeout_ms;
+ if(duringconnect) {
+ timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
+ data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
+ ctimeleft_ms = ctimeout_ms -
+ Curl_timediff(*nowp, data->progress.t_startsingle);
+ if(!ctimeleft_ms)
+ ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
+ if(!timeleft_ms)
+ return ctimeleft_ms; /* no general timeout, this is it */
}
-
- if(!timeout_ms)
- /* avoid returning 0 as that means no timeout! */
- return -1;
-
- return timeout_ms;
+ /* return minimal time left or max amount already expired */
+ return (ctimeleft_ms < timeleft_ms)? ctimeleft_ms : timeleft_ms;
}
/* Copies connection info into the transfer handle to make it available when
@@ -405,7 +389,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
struct eyeballer *baller;
*pballer = NULL;
- baller = calloc(1, sizeof(*baller) + 1000);
+ baller = calloc(1, sizeof(*baller));
if(!baller)
return CURLE_OUT_OF_MEMORY;
@@ -908,7 +892,7 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
if(result)
return result;
ctx->state = SCFST_WAITING;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SCFST_WAITING:
result = is_connected(cf, data, done);
if(!result && *done) {
diff --git a/libs/libcurl/src/content_encoding.c b/libs/libcurl/src/content_encoding.c
index 2ea5e140be..cccf027489 100644
--- a/libs/libcurl/src/content_encoding.c
+++ b/libs/libcurl/src/content_encoding.c
@@ -365,11 +365,14 @@ static CURLcode gzip_do_init(struct Curl_easy *data,
#ifdef OLD_ZLIB_SUPPORT
/* Skip over the gzip header */
-static enum {
+typedef enum {
GZIP_OK,
GZIP_BAD,
GZIP_UNDERFLOW
-} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
+} gzip_status;
+
+static gzip_status check_gzip_header(unsigned char const *data, ssize_t len,
+ ssize_t *headerlen)
{
int method, flags;
const ssize_t totallen = len;
@@ -832,8 +835,8 @@ static const struct Curl_cwtype identity_encoding = {
};
-/* supported content encodings table. */
-static const struct Curl_cwtype * const encodings[] = {
+/* supported general content decoders. */
+static const struct Curl_cwtype * const general_unencoders[] = {
&identity_encoding,
#ifdef HAVE_LIBZ
&deflate_encoding,
@@ -848,6 +851,13 @@ static const struct Curl_cwtype * const encodings[] = {
NULL
};
+/* supported content decoders only for transfer encodings */
+static const struct Curl_cwtype * const transfer_unencoders[] = {
+#ifndef CURL_DISABLE_HTTP
+ &Curl_httpchunk_unencoder,
+#endif
+ NULL
+};
/* Provide a list of comma-separated names of supported encodings.
*/
@@ -861,7 +871,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
DEBUGASSERT(blen);
buf[0] = 0;
- for(cep = encodings; *cep; cep++) {
+ for(cep = general_unencoders; *cep; cep++) {
ce = *cep;
if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
len += strlen(ce->name) + 2;
@@ -873,7 +883,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
}
else if(blen > len) {
char *p = buf;
- for(cep = encodings; *cep; cep++) {
+ for(cep = general_unencoders; *cep; cep++) {
ce = *cep;
if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
strcpy(p, ce->name);
@@ -931,12 +941,23 @@ static const struct Curl_cwtype error_writer = {
};
/* Find the content encoding by name. */
-static const struct Curl_cwtype *find_encoding(const char *name,
- size_t len)
+static const struct Curl_cwtype *find_unencode_writer(const char *name,
+ size_t len,
+ Curl_cwriter_phase phase)
{
const struct Curl_cwtype * const *cep;
- for(cep = encodings; *cep; cep++) {
+ if(phase == CURL_CW_TRANSFER_DECODE) {
+ for(cep = transfer_unencoders; *cep; cep++) {
+ const struct Curl_cwtype *ce = *cep;
+ if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
+ (ce->alias && strncasecompare(name, ce->alias, len)
+ && !ce->alias[len]))
+ return ce;
+ }
+ }
+ /* look among the general decoders */
+ for(cep = general_unencoders; *cep; cep++) {
const struct Curl_cwtype *ce = *cep;
if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
(ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
@@ -950,7 +971,6 @@ static const struct Curl_cwtype *find_encoding(const char *name,
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int is_transfer)
{
- struct SingleRequest *k = &data->req;
Curl_cwriter_phase phase = is_transfer?
CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE;
CURLcode result;
@@ -969,16 +989,14 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
if(!ISSPACE(*enclist))
namelen = enclist - name + 1;
- /* Special case: chunked encoding is handled at the reader level. */
- if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) {
- k->chunk = TRUE; /* chunks coming our way. */
- Curl_httpchunk_init(data); /* init our chunky engine. */
- }
- else if(namelen) {
+ if(namelen) {
const struct Curl_cwtype *cwt;
struct Curl_cwriter *writer;
- if((is_transfer && !data->set.http_transfer_encoding) ||
+ /* if we skip the decoding in this phase, do not look further.
+ * Exception is "chunked" transfer-encoding which always must happen */
+ if((is_transfer && !data->set.http_transfer_encoding &&
+ (namelen != 7 || !strncasecompare(name, "chunked", 7))) ||
(!is_transfer && data->set.http_ce_skip)) {
/* not requested, ignore */
return CURLE_OK;
@@ -990,7 +1008,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
return CURLE_BAD_CONTENT_ENCODING;
}
- cwt = find_encoding(name, namelen);
+ cwt = find_unencode_writer(name, namelen, phase);
if(!cwt)
cwt = &error_writer; /* Defer error at use. */
diff --git a/libs/libcurl/src/cookie.c b/libs/libcurl/src/cookie.c
index 03f9699bd4..73ca6282d5 100644
--- a/libs/libcurl/src/cookie.c
+++ b/libs/libcurl/src/cookie.c
@@ -365,7 +365,7 @@ static void strstore(char **str, const char *newstr, size_t len)
DEBUGASSERT(newstr);
DEBUGASSERT(str);
free(*str);
- *str = Curl_strndup(newstr, len);
+ *str = Curl_memdup0(newstr, len);
}
/*
@@ -821,10 +821,8 @@ Curl_cookie_add(struct Curl_easy *data,
endslash = memrchr(path, '/', (queryp - path));
if(endslash) {
size_t pathlen = (endslash-path + 1); /* include end slash */
- co->path = malloc(pathlen + 1); /* one extra for the zero byte */
+ co->path = Curl_memdup0(path, pathlen);
if(co->path) {
- memcpy(co->path, path, pathlen);
- co->path[pathlen] = 0; /* null-terminate */
co->spath = sanitize_cookie_path(co->path);
if(!co->spath)
badcookie = TRUE; /* out of memory bad */
@@ -927,7 +925,7 @@ Curl_cookie_add(struct Curl_easy *data,
if(!co->spath)
badcookie = TRUE;
fields++; /* add a field and fall down to secure */
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 3:
co->secure = FALSE;
if(strcasecompare(ptr, "TRUE")) {
@@ -1229,7 +1227,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
if(data) {
FILE *fp = NULL;
- if(file) {
+ if(file && *file) {
if(!strcmp(file, "-"))
fp = stdin;
else {
diff --git a/libs/libcurl/src/curl_config.h.cmake b/libs/libcurl/src/curl_config.h.cmake
index 11fa806dd1..f0f6b95c98 100644
--- a/libs/libcurl/src/curl_config.h.cmake
+++ b/libs/libcurl/src/curl_config.h.cmake
@@ -298,12 +298,6 @@
/* if you have the GNU gssapi libraries */
#cmakedefine HAVE_GSSGNU 1
-/* if you have the Heimdal gssapi libraries */
-#cmakedefine HAVE_GSSHEIMDAL 1
-
-/* if you have the MIT gssapi libraries */
-#cmakedefine HAVE_GSSMIT 1
-
/* Define to 1 if you have the `idna_strerror' function. */
#cmakedefine HAVE_IDNA_STRERROR 1
@@ -599,18 +593,9 @@
/* Define to 1 if you have the <utime.h> header file. */
#cmakedefine HAVE_UTIME_H 1
-/* Define to 1 if you have the windows.h header file. */
-#cmakedefine HAVE_WINDOWS_H 1
-
-/* Define to 1 if you have the winsock2.h header file. */
-#cmakedefine HAVE_WINSOCK2_H 1
-
/* Define this symbol if your OS supports changing the contents of argv */
#cmakedefine HAVE_WRITABLE_ARGV 1
-/* Define to 1 if you have the ws2tcpip.h header file. */
-#cmakedefine HAVE_WS2TCPIP_H 1
-
/* Define to 1 if you need the lber.h header file even with ldap.h */
#cmakedefine NEED_LBER_H 1
@@ -713,9 +698,6 @@ ${SIZEOF_TIME_T_CODE}
/* if libPSL is in use */
#cmakedefine USE_LIBPSL 1
-/* If you want to build curl with the built-in manual */
-#cmakedefine USE_MANUAL 1
-
/* if you want to use OpenLDAP code instead of legacy ldap implementation */
#cmakedefine USE_OPENLDAP 1
diff --git a/libs/libcurl/src/curl_config.h.in b/libs/libcurl/src/curl_config.h.in
index ee2c680f6d..d41e70b937 100644
--- a/libs/libcurl/src/curl_config.h.in
+++ b/libs/libcurl/src/curl_config.h.in
@@ -323,12 +323,6 @@
/* if you have GNU GSS */
#undef HAVE_GSSGNU
-/* if you have Heimdal */
-#undef HAVE_GSSHEIMDAL
-
-/* if you have MIT Kerberos */
-#undef HAVE_GSSMIT
-
/* Define to 1 if you have the <hyper.h> header file. */
#undef HAVE_HYPER_H
@@ -493,6 +487,9 @@
/* Define to 1 if you have the <openssl/pem.h> header file. */
#undef HAVE_OPENSSL_PEM_H
+/* if you have the functions OSSL_QUIC_client_method */
+#undef HAVE_OPENSSL_QUIC
+
/* Define to 1 if you have the <openssl/rsa.h> header file. */
#undef HAVE_OPENSSL_RSA_H
@@ -602,6 +599,10 @@
/* Define to 1 if you have the `SSL_set0_wbio' function. */
#undef HAVE_SSL_SET0_WBIO
+/* Define to 1 if you have the `SSL_set_quic_use_legacy_codepoint' function.
+ */
+#undef HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT
+
/* Define to 1 if you have the <stdatomic.h> header file. */
#undef HAVE_STDATOMIC_H
@@ -722,12 +723,6 @@
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
-/* Define to 1 if you have the windows.h header file. */
-#undef HAVE_WINDOWS_H
-
-/* Define to 1 if you have the winsock2.h header file. */
-#undef HAVE_WINSOCK2_H
-
/* Define to 1 if you have the <wolfssh/ssh.h> header file. */
#undef HAVE_WOLFSSH_SSH_H
@@ -746,9 +741,6 @@
/* Define this symbol if your OS supports changing the contents of argv */
#undef HAVE_WRITABLE_ARGV
-/* Define to 1 if you have the ws2tcpip.h header file. */
-#undef HAVE_WS2TCPIP_H
-
/* Define to 1 if you have the <x509.h> header file. */
#undef HAVE_X509_H
@@ -886,6 +878,9 @@
/* if ngtcp2 is in use */
#undef USE_NGTCP2
+/* if ngtcp2_crypto_boringssl is in use */
+#undef USE_NGTCP2_CRYPTO_BORINGSSL
+
/* if ngtcp2_crypto_gnutls is in use */
#undef USE_NGTCP2_CRYPTO_GNUTLS
@@ -895,12 +890,21 @@
/* if ngtcp2_crypto_wolfssl is in use */
#undef USE_NGTCP2_CRYPTO_WOLFSSL
+/* if ngtcp2 + nghttp3 is in use */
+#undef USE_NGTCP2_H3
+
/* Use OpenLDAP-specific code */
#undef USE_OPENLDAP
/* if OpenSSL is in use */
#undef USE_OPENSSL
+/* if openssl quic + nghttp3 is in use */
+#undef USE_OPENSSL_H3
+
+/* if openssl QUIC is in use */
+#undef USE_OPENSSL_QUIC
+
/* if quiche is in use */
#undef USE_QUICHE
diff --git a/libs/libcurl/src/curl_ntlm_wb.c b/libs/libcurl/src/curl_ntlm_wb.c
index 3103b9e3f4..68d2994154 100644
--- a/libs/libcurl/src/curl_ntlm_wb.c
+++ b/libs/libcurl/src/curl_ntlm_wb.c
@@ -266,7 +266,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
size_t len_in = strlen(input), len_out = 0;
struct dynbuf b;
char *ptr = NULL;
- unsigned char *buf = (unsigned char *)data->state.buffer;
+ usigned char buf[1024]
Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
while(len_in > 0) {
@@ -284,7 +284,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
/* Read one line */
while(1) {
ssize_t size =
- wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size);
+ wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, sizeof(buf));
if(size == -1) {
if(errno == EINTR)
continue;
@@ -481,7 +481,7 @@ CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn,
/* connection is already authenticated,
* don't send a header in future requests */
*state = NTLMSTATE_LAST;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case NTLMSTATE_LAST:
Curl_safefree(*allocuserpwd);
authp->done = TRUE;
diff --git a/libs/libcurl/src/curl_printf.h b/libs/libcurl/src/curl_printf.h
index a3c3ce773f..35a3bf81fc 100644
--- a/libs/libcurl/src/curl_printf.h
+++ b/libs/libcurl/src/curl_printf.h
@@ -31,6 +31,10 @@
#include <curl/mprintf.h>
+#define MERR_OK 0
+#define MERR_MEM 1
+#define MERR_TOO_LARGE 2
+
# undef printf
# undef fprintf
# undef msnprintf
diff --git a/libs/libcurl/src/curl_rtmp.c b/libs/libcurl/src/curl_rtmp.c
index 0af8cdeb1a..a4b60175e7 100644
--- a/libs/libcurl/src/curl_rtmp.c
+++ b/libs/libcurl/src/curl_rtmp.c
@@ -79,7 +79,7 @@ const struct Curl_handler Curl_handler_rtmp = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMP, /* defport */
@@ -102,7 +102,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPT, /* defport */
@@ -125,7 +125,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMP, /* defport */
@@ -148,7 +148,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPT, /* defport */
@@ -171,7 +171,7 @@ const struct Curl_handler Curl_handler_rtmps = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPS, /* defport */
@@ -194,7 +194,7 @@ const struct Curl_handler Curl_handler_rtmpts = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtmp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTMPS, /* defport */
diff --git a/libs/libcurl/src/curl_sasl.c b/libs/libcurl/src/curl_sasl.c
index 8d78d49129..8ec942cd1c 100644
--- a/libs/libcurl/src/curl_sasl.c
+++ b/libs/libcurl/src/curl_sasl.c
@@ -205,18 +205,23 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
sasl->force_ir = FALSE; /* Respect external option */
if(auth != CURLAUTH_BASIC) {
- sasl->resetprefs = FALSE;
- sasl->prefmech = SASL_AUTH_NONE;
+ unsigned short mechs = SASL_AUTH_NONE;
+
+ /* If some usable http authentication options have been set, determine
+ new defaults from them. */
if(auth & CURLAUTH_BASIC)
- sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
+ mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
if(auth & CURLAUTH_DIGEST)
- sasl->prefmech |= SASL_MECH_DIGEST_MD5;
+ mechs |= SASL_MECH_DIGEST_MD5;
if(auth & CURLAUTH_NTLM)
- sasl->prefmech |= SASL_MECH_NTLM;
+ mechs |= SASL_MECH_NTLM;
if(auth & CURLAUTH_BEARER)
- sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
+ mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
if(auth & CURLAUTH_GSSAPI)
- sasl->prefmech |= SASL_MECH_GSSAPI;
+ mechs |= SASL_MECH_GSSAPI;
+
+ if(mechs != SASL_AUTH_NONE)
+ sasl->prefmech = mechs;
}
}
diff --git a/libs/libcurl/src/curl_setup.h b/libs/libcurl/src/curl_setup.h
index 98e0853cb0..f907684b2d 100644
--- a/libs/libcurl/src/curl_setup.h
+++ b/libs/libcurl/src/curl_setup.h
@@ -28,6 +28,13 @@
#define CURL_NO_OLDIES
#endif
+/* FIXME: Delete this once the warnings have been fixed. */
+#if !defined(CURL_WARN_SIGN_CONVERSION)
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+#endif
+
/* Set default _WIN32_WINNT */
#ifdef __MINGW32__
#include <_mingw.h>
@@ -53,6 +60,16 @@
# ifndef NOGDI
# define NOGDI
# endif
+/* Detect Windows App environment which has a restricted access
+ * to the Win32 APIs. */
+# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \
+ defined(WINAPI_FAMILY)
+# include <winapifamily.h>
+# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
+ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define CURL_WINDOWS_APP
+# endif
+# endif
#endif
/*
@@ -243,12 +260,39 @@
* Windows setup file includes some system headers.
*/
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
# include "setup-win32.h"
#endif
#include <curl/system.h>
+/* curl uses its own printf() function internally. It understands the GNU
+ * format. Use this format, so that is matches the GNU format attribute we
+ * use with the mingw compiler, allowing it to verify them at compile-time.
+ */
+#ifdef __MINGW32__
+# undef CURL_FORMAT_CURL_OFF_T
+# undef CURL_FORMAT_CURL_OFF_TU
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+#endif
+
+/* based on logic in "curl/mprintf.h" */
+
+#if (defined(__GNUC__) || defined(__clang__)) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+ !defined(CURL_NO_FMT_CHECKS)
+#if defined(__MINGW32__) && !defined(__clang__)
+#define CURL_PRINTF(fmt, arg) \
+ __attribute__((format(gnu_printf, fmt, arg)))
+#else
+#define CURL_PRINTF(fmt, arg) \
+ __attribute__((format(__printf__, fmt, arg)))
+#endif
+#else
+#define CURL_PRINTF(fmt, arg)
+#endif
+
/*
* Use getaddrinfo to resolve the IPv4 address literal. If the current network
* interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64,
@@ -398,6 +442,24 @@
#define SIZEOF_TIME_T 4
#endif
+#ifndef SIZEOF_CURL_SOCKET_T
+/* configure and cmake check and set the define */
+# ifdef _WIN64
+# define SIZEOF_CURL_SOCKET_T 8
+# else
+/* default guess */
+# define SIZEOF_CURL_SOCKET_T 4
+# endif
+#endif
+
+#if SIZEOF_CURL_SOCKET_T < 8
+# define CURL_FORMAT_SOCKET_T "d"
+#elif defined(__MINGW32__)
+# define CURL_FORMAT_SOCKET_T "zd"
+#else
+# define CURL_FORMAT_SOCKET_T "qd"
+#endif
+
/*
* Default sizeof(off_t) in case it hasn't been defined in config file.
*/
@@ -638,6 +700,17 @@
#endif
#endif
+/* fallthrough attribute */
+
+#if !defined(FALLTHROUGH)
+#if (defined(__GNUC__) && __GNUC__ >= 7) || \
+ (defined(__clang__) && __clang_major__ >= 10)
+# define FALLTHROUGH() __attribute__((fallthrough))
+#else
+# define FALLTHROUGH() do {} while (0)
+#endif
+#endif
+
/*
* Include macros and defines that should only be processed once.
*/
@@ -659,10 +732,7 @@
*/
#if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)
-# if defined(SOCKET) || \
- defined(USE_WINSOCK) || \
- defined(HAVE_WINSOCK2_H) || \
- defined(HAVE_WS2TCPIP_H)
+# if defined(SOCKET) || defined(USE_WINSOCK)
# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!"
# endif
#endif
@@ -757,7 +827,13 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
#endif
#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
+ (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \
defined(USE_QUICHE) || defined(USE_MSH3)
+
+#ifdef CURL_WITH_MULTI_SSL
+#error "Multi-SSL combined with QUIC is not supported"
+#endif
+
#define ENABLE_QUIC
#define USE_HTTP3
#endif
diff --git a/libs/libcurl/src/curl_trc.c b/libs/libcurl/src/curl_trc.c
index 9913ceaf9a..4e9b099f16 100644
--- a/libs/libcurl/src/curl_trc.c
+++ b/libs/libcurl/src/curl_trc.c
@@ -157,8 +157,10 @@ static struct Curl_cftype *cf_types[] = {
#endif
#ifdef USE_SSL
&Curl_cft_ssl,
+#ifndef CURL_DISABLE_PROXY
&Curl_cft_ssl_proxy,
#endif
+#endif
#if !defined(CURL_DISABLE_PROXY)
#if !defined(CURL_DISABLE_HTTP)
&Curl_cft_h1_proxy,
diff --git a/libs/libcurl/src/curl_trc.h b/libs/libcurl/src/curl_trc.h
index 27fc8fc769..3da00690e2 100644
--- a/libs/libcurl/src/curl_trc.h
+++ b/libs/libcurl/src/curl_trc.h
@@ -58,14 +58,7 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
* 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
+ const char *fmt, ...) CURL_PRINTF(2, 3);
#define failf Curl_failf
@@ -102,26 +95,14 @@ void Curl_failf(struct Curl_easy *data,
* Output an informational message when transfer's verbose logging is enabled.
*/
void Curl_infof(struct Curl_easy *data,
-#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \
- !defined(__MINGW32__)
- const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-#else
- const char *fmt, ...);
-#endif
+ const char *fmt, ...) CURL_PRINTF(2, 3);
/**
* 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(CURL_HAVE_C99) && \
- !defined(__MINGW32__)
- const char *fmt, ...)
- __attribute__((format(printf, 3, 4)));
-#else
- const char *fmt, ...);
-#endif
+ const char *fmt, ...) CURL_PRINTF(3, 4);
#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
/* All informational messages are not compiled in for size savings */
diff --git a/libs/libcurl/src/dict.c b/libs/libcurl/src/dict.c
index 0d1208bc58..71549ccbb5 100644
--- a/libs/libcurl/src/dict.c
+++ b/libs/libcurl/src/dict.c
@@ -89,7 +89,7 @@ const struct Curl_handler Curl_handler_dict = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_DICT, /* defport */
@@ -123,6 +123,9 @@ static char *unescape_word(const char *input)
/* sendf() sends formatted data to the server */
static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(3, 4);
+
+static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
const char *fmt, ...)
{
ssize_t bytes_written;
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c
index 0abeb965cd..6bc2c43b10 100644
--- a/libs/libcurl/src/doh.c
+++ b/libs/libcurl/src/doh.c
@@ -218,7 +218,6 @@ static CURLcode dohprobe(struct Curl_easy *data,
struct curl_slist *headers)
{
struct Curl_easy *doh = NULL;
- char *nurl = NULL;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
@@ -351,11 +350,9 @@ static CURLcode dohprobe(struct Curl_easy *data,
}
else
goto error;
- free(nurl);
return CURLE_OK;
error:
- free(nurl);
Curl_close(&doh);
return result;
}
@@ -447,7 +444,7 @@ static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
return DOH_DNS_BAD_LABEL;
if(dohlen < (*indexp + 1 + length))
return DOH_DNS_OUT_OF_RANGE;
- *indexp += 1 + length;
+ *indexp += (unsigned int)(1 + length);
} while(length);
return DOH_OK;
}
@@ -459,14 +456,15 @@ static unsigned short get16bit(const unsigned char *doh, int index)
static unsigned int get32bit(const unsigned char *doh, int index)
{
- /* make clang and gcc optimize this to bswap by incrementing
- the pointer first. */
- doh += index;
-
- /* avoid undefined behavior by casting to unsigned before shifting
- 24 bits, possibly into the sign bit. codegen is same, but
- ub sanitizer won't be upset */
- return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
+ /* make clang and gcc optimize this to bswap by incrementing
+ the pointer first. */
+ doh += index;
+
+ /* avoid undefined behavior by casting to unsigned before shifting
+ 24 bits, possibly into the sign bit. codegen is same, but
+ ub sanitizer won't be upset */
+ return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
+ ((unsigned)doh[2] << 8) | doh[3];
}
static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
@@ -904,7 +902,6 @@ UNITTEST void de_cleanup(struct dohentry *d)
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp)
{
- struct connectdata *conn = data->conn;
CURLcode result;
struct dohdata *dohp = data->req.doh;
*dnsp = NULL; /* defaults to no response */
@@ -913,7 +910,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
!dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
- failf(data, "Could not DoH-resolve: %s", conn->resolve_async.hostname);
+ failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
CURLE_COULDNT_RESOLVE_HOST;
}
@@ -976,7 +973,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
Curl_freeaddrinfo(ai);
}
else {
- conn->resolve_async.dns = dns;
+ data->state.async.dns = dns;
*dnsp = dns;
result = CURLE_OK; /* address resolution OK */
}
diff --git a/libs/libcurl/src/dynbuf.c b/libs/libcurl/src/dynbuf.c
index dcf3da23ac..16a47108ca 100644
--- a/libs/libcurl/src/dynbuf.c
+++ b/libs/libcurl/src/dynbuf.c
@@ -81,7 +81,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
if(fit > s->toobig) {
Curl_dyn_free(s);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_TOO_LARGE;
}
else if(!a) {
DEBUGASSERT(!indx);
@@ -199,6 +199,9 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
if(!rc)
return CURLE_OK;
+ else if(rc == MERR_TOO_LARGE)
+ return CURLE_TOO_LARGE;
+ return CURLE_OUT_OF_MEMORY;
#else
char *str;
str = vaprintf(fmt, ap); /* this allocs a new string to append */
@@ -210,8 +213,8 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
}
/* If we failed, we cleanup the whole buffer and return error */
Curl_dyn_free(s);
+ return CURLE_OK;
#endif
- return CURLE_OUT_OF_MEMORY;
}
/*
diff --git a/libs/libcurl/src/dynbuf.h b/libs/libcurl/src/dynbuf.h
index 76b551d93b..2fd7d7e0ae 100644
--- a/libs/libcurl/src/dynbuf.h
+++ b/libs/libcurl/src/dynbuf.h
@@ -61,9 +61,9 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
WARN_UNUSED_RESULT;
CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
- WARN_UNUSED_RESULT;
+ WARN_UNUSED_RESULT CURL_PRINTF(2, 3);
CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
- WARN_UNUSED_RESULT;
+ WARN_UNUSED_RESULT CURL_PRINTF(2, 0);
void Curl_dyn_reset(struct dynbuf *s);
CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
diff --git a/libs/libcurl/src/easy.c b/libs/libcurl/src/easy.c
index f5ffe259de..5aa564c808 100644
--- a/libs/libcurl/src/easy.c
+++ b/libs/libcurl/src/easy.c
@@ -480,13 +480,15 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
ev->list = nxt;
free(m);
m = nxt;
- infof(easy, "socket cb: socket %d REMOVED", s);
+ infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+ " REMOVED", s);
}
else {
/* The socket 's' is already being monitored, update the activity
mask. Convert from libcurl bitmask to the poll one. */
m->socket.events = socketcb2poll(what);
- infof(easy, "socket cb: socket %d UPDATED as %s%s", s,
+ infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+ " UPDATED as %s%s", s,
(what&CURL_POLL_IN)?"IN":"",
(what&CURL_POLL_OUT)?"OUT":"");
}
@@ -510,7 +512,8 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
m->socket.events = socketcb2poll(what);
m->socket.revents = 0;
ev->list = m;
- infof(easy, "socket cb: socket %d ADDED as %s%s", s,
+ infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+ " ADDED as %s%s", s,
(what&CURL_POLL_IN)?"IN":"",
(what&CURL_POLL_OUT)?"OUT":"");
}
@@ -599,8 +602,9 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
if(fds[i].revents) {
/* socket activity, tell libcurl */
int act = poll2cselect(fds[i].revents); /* convert */
- infof(multi->easyp, "call curl_multi_socket_action(socket %d)",
- fds[i].fd);
+ infof(multi->easyp,
+ "call curl_multi_socket_action(socket "
+ "%" CURL_FORMAT_SOCKET_T ")", fds[i].fd);
mcode = curl_multi_socket_action(multi, fds[i].fd, act,
&ev->running_handles);
}
@@ -684,9 +688,9 @@ static CURLcode easy_transfer(struct Curl_multi *multi)
/* Make sure to return some kind of error if there was a multi problem */
if(mcode) {
result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
- /* The other multi errors should never happen, so return
- something suitably generic */
- CURLE_BAD_FUNCTION_ARGUMENT;
+ /* The other multi errors should never happen, so return
+ something suitably generic */
+ CURLE_BAD_FUNCTION_ARGUMENT;
}
return result;
@@ -973,6 +977,36 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
}
#endif
+#ifdef CURLRES_ASYNCH
+ /* Clone the resolver handle, if present, for the new handle */
+ if(Curl_resolver_duphandle(outcurl,
+ &outcurl->state.async.resolver,
+ data->state.async.resolver))
+ goto fail;
+#endif
+
+#ifdef USE_ARES
+ {
+ CURLcode rc;
+
+ rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+ }
+#endif /* USE_ARES */
+
Curl_initinfo(outcurl);
outcurl->magic = CURLEASY_MAGIC_NUMBER;
@@ -1111,7 +1145,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
if(!data->state.tempcount)
/* if not pausing again, force a recv/send check of this connection as
the data might've been read off the socket already */
- data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
if(data->multi) {
if(Curl_update_timer(data->multi))
return CURLE_ABORTED_BY_CALLBACK;
diff --git a/libs/libcurl/src/easyoptions.c b/libs/libcurl/src/easyoptions.c
index deade8ed71..be6073a90c 100644
--- a/libs/libcurl/src/easyoptions.c
+++ b/libs/libcurl/src/easyoptions.c
@@ -274,6 +274,8 @@ struct curl_easyoption Curl_easyopts[] = {
{"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0},
{"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
CURLOT_LONG, 0},
+ {"SERVER_RESPONSE_TIMEOUT_MS", CURLOPT_SERVER_RESPONSE_TIMEOUT_MS,
+ CURLOT_LONG, 0},
{"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0},
{"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0},
{"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0},
@@ -373,6 +375,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (323 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (324 + 1));
}
#endif
diff --git a/libs/libcurl/src/file.c b/libs/libcurl/src/file.c
index 7e0570d9f9..b15b6ab408 100644
--- a/libs/libcurl/src/file.c
+++ b/libs/libcurl/src/file.c
@@ -113,7 +113,7 @@ const struct Curl_handler Curl_handler_file = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
file_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
0, /* defport */
@@ -290,16 +290,15 @@ static CURLcode file_upload(struct Curl_easy *data)
int fd;
int mode;
CURLcode result = CURLE_OK;
- char *buf = data->state.buffer;
+ char buffer[8*1024], *uphere_save;
curl_off_t bytecount = 0;
struct_stat file_stat;
- const char *buf2;
+ const char *sendbuf;
/*
* Since FILE: doesn't do the full init, we need to provide some extra
* assignments here.
*/
- data->req.upload_fromhere = buf;
if(!dir)
return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
@@ -338,11 +337,15 @@ static CURLcode file_upload(struct Curl_easy *data)
data->state.resume_from = (curl_off_t)file_stat.st_size;
}
+ /* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ
+ * client data to! Please, someone fix... */
+ uphere_save = data->req.upload_fromhere;
while(!result) {
size_t nread;
ssize_t nwrite;
size_t readcount;
- result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount);
+ data->req.upload_fromhere = buffer;
+ result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount);
if(result)
break;
@@ -356,19 +359,19 @@ static CURLcode file_upload(struct Curl_easy *data)
if((curl_off_t)nread <= data->state.resume_from) {
data->state.resume_from -= nread;
nread = 0;
- buf2 = buf;
+ sendbuf = buffer;
}
else {
- buf2 = buf + data->state.resume_from;
+ sendbuf = buffer + data->state.resume_from;
nread -= (size_t)data->state.resume_from;
data->state.resume_from = 0;
}
}
else
- buf2 = buf;
+ sendbuf = buffer;
/* write the data to the target */
- nwrite = write(fd, buf2, nread);
+ nwrite = write(fd, sendbuf, nread);
if((size_t)nwrite != nread) {
result = CURLE_SEND_ERROR;
break;
@@ -387,6 +390,7 @@ static CURLcode file_upload(struct Curl_easy *data)
result = CURLE_ABORTED_BY_CALLBACK;
close(fd);
+ data->req.upload_fromhere = uphere_save;
return result;
}
@@ -413,14 +417,11 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
curl_off_t expected_size = -1;
bool size_known;
bool fstated = FALSE;
- char *buf = data->state.buffer;
int fd;
struct FILEPROTO *file;
*done = TRUE; /* unconditionally */
- Curl_pgrsStartNow(data);
-
if(data->state.upload)
return file_upload(data);
@@ -543,21 +544,22 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
while(!result) {
+ char tmpbuf[8*1024];
ssize_t nread;
/* Don't fill a whole buffer if we want less than all data */
size_t bytestoread;
if(size_known) {
- bytestoread = (expected_size < data->set.buffer_size) ?
- curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
+ bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ?
+ curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1);
}
else
- bytestoread = data->set.buffer_size-1;
+ bytestoread = sizeof(tmpbuf)-1;
- nread = read(fd, buf, bytestoread);
+ nread = read(fd, tmpbuf, bytestoread);
if(nread > 0)
- buf[nread] = 0;
+ tmpbuf[nread] = 0;
if(nread <= 0 || (size_known && (expected_size == 0)))
break;
@@ -565,7 +567,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
if(size_known)
expected_size -= nread;
- result = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread);
if(result)
return result;
diff --git a/libs/libcurl/src/formdata.c b/libs/libcurl/src/formdata.c
index 88901c6bdd..1fd05fe186 100644
--- a/libs/libcurl/src/formdata.c
+++ b/libs/libcurl/src/formdata.c
@@ -277,7 +277,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
case CURLFORM_PTRNAME:
current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURLFORM_COPYNAME:
if(current_form->name)
return_value = CURL_FORMADD_OPTION_TWICE;
@@ -303,7 +303,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
*/
case CURLFORM_PTRCONTENTS:
current_form->flags |= HTTPPOST_PTRCONTENTS;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURLFORM_COPYCONTENTS:
if(current_form->value)
return_value = CURL_FORMADD_OPTION_TWICE;
@@ -603,7 +603,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
app passed in a bad combo, so we better check for that first. */
if(form->name) {
/* copy name (without strdup; possibly not null-terminated) */
- form->name = Curl_strndup(form->name, form->namelength?
+ form->name = Curl_memdup0(form->name, form->namelength?
form->namelength:
strlen(form->name));
}
@@ -779,11 +779,9 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
if(!name || !len)
return curl_mime_name(part, name);
- zname = malloc(len + 1);
+ zname = Curl_memdup0(name, len);
if(!zname)
return CURLE_OUT_OF_MEMORY;
- memcpy(zname, name, len);
- zname[len] = '\0';
res = curl_mime_name(part, zname);
free(zname);
return res;
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c
index cee0d97c59..03678ac20c 100644
--- a/libs/libcurl/src/ftp.c
+++ b/libs/libcurl/src/ftp.c
@@ -72,6 +72,7 @@
#include "warnless.h"
#include "http_proxy.h"
#include "socks.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -167,7 +168,7 @@ const struct Curl_handler Curl_handler_ftp = {
ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_FTP, /* defport */
@@ -198,7 +199,7 @@ const struct Curl_handler Curl_handler_ftps = {
ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_FTPS, /* defport */
@@ -362,10 +363,11 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
- int result;
+ int socketstate = 0;
timediff_t timeout_ms;
ssize_t nread;
int ftpcode;
+ bool response = FALSE;
*received = FALSE;
@@ -378,17 +380,21 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
}
/* First check whether there is a cached response from server */
- if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+ if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
/* Data connection could not be established, let's return */
infof(data, "There is negative response in cache while serv connect");
(void)Curl_GetFTPResponse(data, &nread, &ftpcode);
return CURLE_FTP_ACCEPT_FAILED;
}
- result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+ if(pp->overflow)
+ /* there is pending control data still in the buffer to read */
+ response = TRUE;
+ else
+ socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
/* see if the connection request is already here */
- switch(result) {
+ switch(socketstate) {
case -1: /* error */
/* let's die here */
failf(data, "Error while waiting for server connect");
@@ -396,23 +402,23 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
case 0: /* Server connect is not received yet */
break; /* loop */
default:
-
- if(result & CURL_CSELECT_IN2) {
+ if(socketstate & CURL_CSELECT_IN2) {
infof(data, "Ready to accept data connection from server");
*received = TRUE;
}
- else if(result & CURL_CSELECT_IN) {
- infof(data, "Ctrl conn has data while waiting for data conn");
- (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
-
- if(ftpcode/100 > 3)
- return CURLE_FTP_ACCEPT_FAILED;
+ else if(socketstate & CURL_CSELECT_IN)
+ response = TRUE;
+ break;
+ }
+ if(response) {
+ infof(data, "Ctrl conn has data while waiting for data conn");
+ (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
- return CURLE_WEIRD_SERVER_REPLY;
- }
+ if(ftpcode/100 > 3)
+ return CURLE_FTP_ACCEPT_FAILED;
- break;
- } /* switch() */
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
return CURLE_OK;
}
@@ -553,7 +559,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
#ifdef HAVE_GSSAPI
{
struct connectdata *conn = data->conn;
- char * const buf = data->state.buffer;
+ char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
/* handle the security-oriented responses 6xx ***/
switch(code) {
@@ -659,7 +665,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
*
*/
- if(pp->cache && (cache_skip < 2)) {
+ if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
/*
* There's a cache left since before. We then skipping the wait for
* socket action, unless this is the same cache like the previous round
@@ -687,7 +693,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
if(result)
break;
- if(!nread && pp->cache)
+ if(!nread && Curl_dyn_len(&pp->recvbuf))
/* bump cache skip counter as on repeated skips we must wait for more
data */
cache_skip++;
@@ -926,6 +932,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
bool possibly_non_local = TRUE;
char buffer[STRERROR_LEN];
char *addr = NULL;
+ size_t addrlen = 0;
+ char ipstr[50];
/* Step 1, figure out what is requested,
* accepted format :
@@ -934,32 +942,17 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(data->set.str[STRING_FTPPORT] &&
(strlen(data->set.str[STRING_FTPPORT]) > 1)) {
-
-#ifdef ENABLE_IPV6
- size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
- INET6_ADDRSTRLEN : strlen(string_ftpport);
-#else
- size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
- INET_ADDRSTRLEN : strlen(string_ftpport);
-#endif
- char *ip_start = string_ftpport;
char *ip_end = NULL;
- char *port_start = NULL;
- char *port_sep = NULL;
-
- addr = calloc(1, addrlen + 1);
- if(!addr) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
#ifdef ENABLE_IPV6
if(*string_ftpport == '[') {
/* [ipv6]:port(-range) */
- ip_start = string_ftpport + 1;
- ip_end = strchr(string_ftpport, ']');
- if(ip_end)
- strncpy(addr, ip_start, ip_end - ip_start);
+ char *ip_start = string_ftpport + 1;
+ ip_end = strchr(ip_start, ']');
+ if(ip_end) {
+ addrlen = ip_end - ip_start;
+ addr = ip_start;
+ }
}
else
#endif
@@ -969,28 +962,27 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
}
else {
ip_end = strchr(string_ftpport, ':');
+ addr = string_ftpport;
if(ip_end) {
/* either ipv6 or (ipv4|domain|interface):port(-range) */
+ addrlen = ip_end - string_ftpport;
#ifdef ENABLE_IPV6
if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
/* ipv6 */
port_min = port_max = 0;
- strcpy(addr, string_ftpport);
ip_end = NULL; /* this got no port ! */
}
- else
#endif
- /* (ipv4|domain|interface):port(-range) */
- strncpy(addr, string_ftpport, ip_end - ip_start);
}
else
/* ipv4|interface */
- strcpy(addr, string_ftpport);
+ addrlen = strlen(string_ftpport);
}
/* parse the port */
if(ip_end) {
- port_start = strchr(ip_end, ':');
+ char *port_sep = NULL;
+ char *port_start = strchr(ip_end, ':');
if(port_start) {
port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
port_sep = strchr(port_start, '-');
@@ -1011,22 +1003,29 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(port_min > port_max)
port_min = port_max = 0;
- if(*addr != '\0') {
+ if(addrlen) {
+ DEBUGASSERT(addr);
+ if(addrlen >= sizeof(ipstr))
+ goto out;
+ memcpy(ipstr, addr, addrlen);
+ ipstr[addrlen] = 0;
+
/* attempt to get the address of the given interface name */
switch(Curl_if2ip(conn->remote_addr->family,
#ifdef ENABLE_IPV6
Curl_ipv6_scope(&conn->remote_addr->sa_addr),
conn->scope_id,
#endif
- addr, hbuf, sizeof(hbuf))) {
+ ipstr, hbuf, sizeof(hbuf))) {
case IF2IP_NOT_FOUND:
/* not an interface, use the given string as host name instead */
- host = addr;
+ host = ipstr;
break;
case IF2IP_AF_NOT_SUPPORTED:
goto out;
case IF2IP_FOUND:
host = hbuf; /* use the hbuf for host name */
+ break;
}
}
else
@@ -1266,7 +1265,6 @@ out:
}
if(portsock != CURL_SOCKET_BAD)
Curl_socket_close(data, conn, portsock);
- free(addr);
return result;
}
@@ -1589,13 +1587,14 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
+ (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) :
curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.fread_func(scratch, 1, readthisamountnow,
data->state.in);
passed += actuallyread;
@@ -1828,7 +1827,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
struct Curl_dns_entry *addr = NULL;
enum resolve_t rc;
unsigned short connectport; /* the local port connect() should use! */
- char *str = &data->state.buffer[4]; /* start on the first letter */
+ struct pingpong *pp = &ftpc->pp;
+ char *str =
+ Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
/* if we come here again, make sure the former name is cleared */
Curl_safefree(ftpc->newhost);
@@ -2106,8 +2107,9 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
/* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
last .sss part is optional and means fractions of a second */
int year, month, day, hour, minute, second;
- if(ftp_213_date(&data->state.buffer[4],
- &year, &month, &day, &hour, &minute, &second)) {
+ struct pingpong *pp = &ftpc->pp;
+ char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
+ if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
char timebuf[24];
msnprintf(timebuf, sizeof(timebuf),
@@ -2318,7 +2320,8 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
curl_off_t filesize = -1;
- char *buf = data->state.buffer;
+ char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
+ size_t len = data->conn->proto.ftpc.pp.nfinal;
/* get the size from the ascii string: */
if(ftpcode == 213) {
@@ -2326,13 +2329,13 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
for all the digits at the end of the response and parse only those as a
number. */
char *start = &buf[4];
- char *fdigit = strchr(start, '\r');
+ char *fdigit = memchr(start, '\r', len);
if(fdigit) {
- do
+ fdigit--;
+ if(*fdigit == '\n')
+ fdigit--;
+ while(ISDIGIT(fdigit[-1]) && (fdigit > start))
fdigit--;
- while(ISDIGIT(*fdigit) && (fdigit > start));
- if(!ISDIGIT(*fdigit))
- fdigit++;
}
else
fdigit = start;
@@ -2501,7 +2504,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
*
* Example D above makes this parsing a little tricky */
char *bytes;
- char *buf = data->state.buffer;
+ char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
bytes = strstr(buf, " bytes");
if(bytes) {
long in = (long)(--bytes-buf);
@@ -2770,7 +2773,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
case FTP_AUTH:
/* we have gotten the response to a previous AUTH command */
- if(pp->cache_size)
+ if(pp->overflow)
return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
/* RFC2228 (page 5) says:
@@ -2868,14 +2871,11 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
case FTP_PWD:
if(ftpcode == 257) {
- char *ptr = &data->state.buffer[4]; /* start on the first letter */
- const size_t buf_size = data->set.buffer_size;
- char *dir;
+ char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ letter */
bool entry_extracted = FALSE;
-
- dir = malloc(nread + 1);
- if(!dir)
- return CURLE_OUT_OF_MEMORY;
+ struct dynbuf out;
+ Curl_dyn_init(&out, 1000);
/* Reply format is like
257<space>[rubbish]"<directory-name>"<space><commentary> and the
@@ -2887,33 +2887,30 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
*/
/* scan for the first double-quote for non-standard responses */
- while(ptr < &data->state.buffer[buf_size]
- && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
+ while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
ptr++;
if('\"' == *ptr) {
/* it started good */
- char *store;
- ptr++;
- for(store = dir; *ptr;) {
+ for(ptr++; *ptr; ptr++) {
if('\"' == *ptr) {
if('\"' == ptr[1]) {
/* "quote-doubling" */
- *store = ptr[1];
+ result = Curl_dyn_addn(&out, &ptr[1], 1);
ptr++;
}
else {
/* end of path */
- entry_extracted = TRUE;
+ if(Curl_dyn_len(&out))
+ entry_extracted = TRUE;
break; /* get out of this loop */
}
}
else
- *store = *ptr;
- store++;
- ptr++;
+ result = Curl_dyn_addn(&out, ptr, 1);
+ if(result)
+ return result;
}
- *store = '\0'; /* null-terminate */
}
if(entry_extracted) {
/* If the path name does not look like an absolute path (i.e.: it
@@ -2927,6 +2924,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
The method used here is to check the server OS: we do it only
if the path name looks strange to minimize overhead on other
systems. */
+ char *dir = Curl_dyn_ptr(&out);
if(!ftpc->server_os && dir[0] != '/') {
result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
@@ -2951,7 +2949,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
}
else {
/* couldn't get the path */
- free(dir);
+ Curl_dyn_free(&out);
infof(data, "Failed to figure out path");
}
}
@@ -2961,25 +2959,23 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
case FTP_SYST:
if(ftpcode == 215) {
- char *ptr = &data->state.buffer[4]; /* start on the first letter */
+ char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ letter */
char *os;
- char *store;
-
- os = malloc(nread + 1);
- if(!os)
- return CURLE_OUT_OF_MEMORY;
+ char *start;
/* Reply format is like
215<space><OS-name><space><commentary>
*/
while(*ptr == ' ')
ptr++;
- for(store = os; *ptr && *ptr != ' ';)
- *store++ = *ptr++;
- *store = '\0'; /* null-terminate */
+ for(start = ptr; *ptr && *ptr != ' '; ptr++)
+ ;
+ os = Curl_memdup0(start, ptr - start);
+ if(!os)
+ return CURLE_OUT_OF_MEMORY;
/* Check for special servers here. */
-
if(strcasecompare(os, "OS/400")) {
/* Force OS400 name format 1. */
result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
@@ -3131,7 +3127,6 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
break;
case FTP_QUIT:
- /* fallthrough, just stop! */
default:
/* internal error */
ftp_state(data, FTP_STOP);
@@ -3206,8 +3201,7 @@ static CURLcode ftp_connect(struct Curl_easy *data,
conn->bits.ftp_use_control_ssl = TRUE;
}
- Curl_pp_setup(pp); /* once per transfer */
- Curl_pp_init(data, pp); /* init the generic pingpong data */
+ Curl_pp_init(pp); /* once per transfer */
/* When we connect, we start in the state where we await the 220
response */
@@ -3258,14 +3252,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
case CURLE_REMOTE_FILE_NOT_FOUND:
case CURLE_WRITE_ERROR:
/* the connection stays alive fine even though this happened */
- /* fall-through */
case CURLE_OK: /* doesn't affect the control connection's status */
if(!premature)
break;
/* until we cope better with prematurely ended requests, let them
* fallback as if in complete failure */
- /* FALLTHROUGH */
+ FALLTHROUGH();
default: /* by default, an error means the control connection is
wedged and should not be used anymore */
ftpc->ctl_valid = FALSE;
@@ -4177,13 +4170,12 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
}
- ftpc->dirs[0] = calloc(1, dirlen + 1);
+ ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
if(!ftpc->dirs[0]) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
- strncpy(ftpc->dirs[0], rawPath, dirlen);
ftpc->dirdepth = 1; /* we consider it to be a single dir */
fileName = slashPos + 1; /* rest is file name */
}
@@ -4222,12 +4214,11 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
CWD requires a parameter and a non-existent parameter a) doesn't
work on many servers and b) has no effect on the others. */
if(compLen > 0) {
- char *comp = calloc(1, compLen + 1);
+ char *comp = Curl_memdup0(curPos, compLen);
if(!comp) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
- strncpy(comp, curPos, compLen);
ftpc->dirs[ftpc->dirdepth++] = comp;
}
curPos = slashPos + 1;
diff --git a/libs/libcurl/src/getinfo.c b/libs/libcurl/src/getinfo.c
index 28e9c06c1d..0d72f1bdf2 100644
--- a/libs/libcurl/src/getinfo.c
+++ b/libs/libcurl/src/getinfo.c
@@ -409,6 +409,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_STARTTRANSFER_TIME_T:
*param_offt = data->progress.t_starttransfer;
break;
+ case CURLINFO_QUEUE_TIME_T:
+ *param_offt = data->progress.t_postqueue;
+ break;
case CURLINFO_REDIRECT_TIME_T:
*param_offt = data->progress.t_redirect;
break;
@@ -420,7 +423,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
break;
case CURLINFO_CONN_ID:
*param_offt = data->conn?
- data->conn->connection_id : data->state.recent_conn_id;
+ data->conn->connection_id : data->state.recent_conn_id;
break;
default:
return CURLE_UNKNOWN_OPTION;
diff --git a/libs/libcurl/src/gopher.c b/libs/libcurl/src/gopher.c
index f2f016a332..8adf741532 100644
--- a/libs/libcurl/src/gopher.c
+++ b/libs/libcurl/src/gopher.c
@@ -75,7 +75,7 @@ const struct Curl_handler Curl_handler_gopher = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_GOPHER, /* defport */
@@ -99,7 +99,7 @@ const struct Curl_handler Curl_handler_gophers = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_GOPHER, /* defport */
diff --git a/libs/libcurl/src/headers.c b/libs/libcurl/src/headers.c
index bcd65be503..7999ca911d 100644
--- a/libs/libcurl/src/headers.c
+++ b/libs/libcurl/src/headers.c
@@ -185,7 +185,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
}
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
- char **name, char **value)
+ char **name, char **value)
{
char *end = header + hlen - 1; /* point to the last byte */
DEBUGASSERT(hlen);
@@ -292,9 +292,10 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
if(!end) {
end = strchr(header, '\n');
if(!end)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ /* neither CR nor LF as terminator is not a valid header */
+ return CURLE_WEIRD_SERVER_REPLY;
}
- hlen = end - header + 1;
+ hlen = end - header;
if((header[0] == ' ') || (header[0] == '\t')) {
if(data->state.prevhead)
@@ -319,21 +320,19 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
hs->buffer[hlen] = 0; /* nul terminate */
result = namevalue(hs->buffer, hlen, type, &name, &value);
- if(result)
- goto fail;
-
- hs->name = name;
- hs->value = value;
- hs->type = type;
- hs->request = data->state.requests;
-
- /* insert this node into the list of headers */
- Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
- hs, &hs->node);
- data->state.prevhead = hs;
- return CURLE_OK;
-fail:
- free(hs);
+ if(!result) {
+ hs->name = name;
+ hs->value = value;
+ hs->type = type;
+ hs->request = data->state.requests;
+
+ /* insert this node into the list of headers */
+ Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
+ hs, &hs->node);
+ data->state.prevhead = hs;
+ }
+ else
+ free(hs);
return result;
}
diff --git a/libs/libcurl/src/hostasyn.c b/libs/libcurl/src/hostasyn.c
index 326aa29983..59a009ce33 100644
--- a/libs/libcurl/src/hostasyn.c
+++ b/libs/libcurl/src/hostasyn.c
@@ -67,11 +67,10 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
int status,
struct Curl_addrinfo *ai)
{
- struct connectdata *conn = data->conn;
struct Curl_dns_entry *dns = NULL;
CURLcode result = CURLE_OK;
- conn->resolve_async.status = status;
+ data->state.async.status = status;
if(CURL_ASYNC_SUCCESS == status) {
if(ai) {
@@ -79,8 +78,8 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
dns = Curl_cache_addr(data, ai,
- conn->resolve_async.hostname, 0,
- conn->resolve_async.port);
+ data->state.async.hostname, 0,
+ data->state.async.port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -95,12 +94,12 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
}
}
- conn->resolve_async.dns = dns;
+ data->state.async.dns = dns;
/* Set async.done TRUE last in this function since it may be used multi-
threaded and once this is TRUE the other thread may read fields from the
async struct */
- conn->resolve_async.done = TRUE;
+ data->state.async.done = TRUE;
/* IPv4: The input hostent struct will be freed by ares when we return from
this function */
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c
index 8d84708a26..70297ad60b 100644
--- a/libs/libcurl/src/hostip.c
+++ b/libs/libcurl/src/hostip.c
@@ -741,7 +741,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
Curl_set_in_callback(data, true);
st = data->set.resolver_start(
#ifdef USE_CURL_ASYNC
- conn->resolve_async.resolver,
+ data->state.async.resolver,
#else
NULL,
#endif
@@ -754,16 +754,22 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
#ifndef USE_RESOLVE_ON_IPS
/* First check if this is an IPv4 address string */
- if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
/* This is a dotted IP address 123.123.123.123-style */
addr = Curl_ip2addr(AF_INET, &in, hostname, port);
+ if(!addr)
+ return CURLRESOLV_ERROR;
+ }
#ifdef ENABLE_IPV6
- if(!addr) {
+ else {
struct in6_addr in6;
/* check if this is an IPv6 address string */
- if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
+ if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
/* This is an IPv6 address literal */
addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
+ if(!addr)
+ return CURLRESOLV_ERROR;
+ }
}
#endif /* ENABLE_IPV6 */
@@ -1415,9 +1421,9 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
struct connectdata *conn = data->conn;
#ifdef USE_CURL_ASYNC
- if(conn->resolve_async.dns) {
- conn->dns_entry = conn->resolve_async.dns;
- conn->resolve_async.dns = NULL;
+ if(data->state.async.dns) {
+ conn->dns_entry = data->state.async.dns;
+ data->state.async.dns = NULL;
}
#endif
@@ -1439,11 +1445,11 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolver_error(struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
const char *host_or_proxy;
CURLcode result;
#ifndef CURL_DISABLE_PROXY
+ struct connectdata *conn = data->conn;
if(conn->bits.httpproxy) {
host_or_proxy = "proxy";
result = CURLE_COULDNT_RESOLVE_PROXY;
@@ -1456,7 +1462,7 @@ CURLcode Curl_resolver_error(struct Curl_easy *data)
}
failf(data, "Could not resolve %s: %s", host_or_proxy,
- conn->resolve_async.hostname);
+ data->state.async.hostname);
return result;
}
diff --git a/libs/libcurl/src/hsts.c b/libs/libcurl/src/hsts.c
index 9ccfa14905..96af618bde 100644
--- a/libs/libcurl/src/hsts.c
+++ b/libs/libcurl/src/hsts.c
@@ -117,8 +117,6 @@ static CURLcode hsts_create(struct hsts *h,
bool subdomains,
curl_off_t expires)
{
- struct stsentry *sts;
- char *duphost;
size_t hlen;
DEBUGASSERT(h);
DEBUGASSERT(hostname);
@@ -127,24 +125,23 @@ static CURLcode hsts_create(struct hsts *h,
if(hlen && (hostname[hlen - 1] == '.'))
/* strip off any trailing dot */
--hlen;
- if(!hlen)
- /* no host name left */
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- sts = hsts_entry();
- if(!sts)
- return CURLE_OUT_OF_MEMORY;
+ if(hlen) {
+ char *duphost;
+ struct stsentry *sts = hsts_entry();
+ if(!sts)
+ return CURLE_OUT_OF_MEMORY;
+
+ duphost = Curl_memdup0(hostname, hlen);
+ if(!duphost) {
+ free(sts);
+ return CURLE_OUT_OF_MEMORY;
+ }
- duphost = Curl_strndup(hostname, hlen);
- if(!duphost) {
- free(sts);
- return CURLE_OUT_OF_MEMORY;
+ sts->host = duphost;
+ sts->expires = expires;
+ sts->includeSubDomains = subdomains;
+ Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
}
-
- sts->host = duphost;
- sts->expires = expires;
- sts->includeSubDomains = subdomains;
- Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
return CURLE_OK;
}
@@ -481,6 +478,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
if(sc == CURLSTS_OK) {
time_t expires;
CURLcode result;
+ DEBUGASSERT(e.name[0]);
if(!e.name[0])
/* bail out if no name was stored */
return CURLE_BAD_FUNCTION_ARGUMENT;
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c
index d27c300bd3..6741425ae5 100644
--- a/libs/libcurl/src/http.c
+++ b/libs/libcurl/src/http.c
@@ -100,24 +100,14 @@
* Forward declarations.
*/
-static int http_getsock_do(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks);
static bool http_should_fail(struct Curl_easy *data);
-static CURLcode http_setup_conn(struct Curl_easy *data,
- struct connectdata *conn);
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
- struct connectdata *conn);
-#endif
-
/*
* HTTP handler interface.
*/
const struct Curl_handler Curl_handler_http = {
"HTTP", /* scheme */
- http_setup_conn, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -125,11 +115,11 @@ const struct Curl_handler Curl_handler_http = {
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ Curl_http_write_resp, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTP, /* defport */
@@ -139,39 +129,13 @@ const struct Curl_handler Curl_handler_http = {
PROTOPT_USERPWDCTRL
};
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_ws = {
- "WS", /* scheme */
- ws_setup_conn, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- Curl_http_connect, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- ZERO_NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ZERO_NULL, /* perform_getsock */
- Curl_ws_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ZERO_NULL, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_WS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_CREDSPERREQUEST | /* flags */
- PROTOPT_USERPWDCTRL
-};
-#endif
-
#ifdef USE_SSL
/*
* HTTPS handler interface.
*/
const struct Curl_handler Curl_handler_https = {
"HTTPS", /* scheme */
- http_setup_conn, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -179,11 +143,11 @@ const struct Curl_handler Curl_handler_https = {
NULL, /* connecting */
ZERO_NULL, /* doing */
NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ Curl_http_write_resp, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTPS, /* defport */
@@ -193,36 +157,10 @@ const struct Curl_handler Curl_handler_https = {
PROTOPT_USERPWDCTRL
};
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_wss = {
- "WSS", /* scheme */
- ws_setup_conn, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- Curl_http_connect, /* connect_it */
- NULL, /* connecting */
- ZERO_NULL, /* doing */
- NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ZERO_NULL, /* perform_getsock */
- Curl_ws_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ZERO_NULL, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTPS, /* defport */
- CURLPROTO_WSS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
- PROTOPT_USERPWDCTRL
-};
-#endif
-
#endif
-static CURLcode http_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
+CURLcode Curl_http_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
{
/* allocate the HTTP-specific struct for the Curl_easy, only to survive
during this request */
@@ -245,16 +183,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
return CURLE_OK;
}
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
-{
- /* websockets is 1.1 only (for now) */
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
- return http_setup_conn(data, conn);
-}
-#endif
-
#ifndef CURL_DISABLE_PROXY
/*
* checkProxyHeaders() checks the linked list of custom proxy headers
@@ -297,7 +225,6 @@ char *Curl_copy_header_value(const char *header)
{
const char *start;
const char *end;
- char *value;
size_t len;
/* Find the end of the header name */
@@ -330,14 +257,7 @@ char *Curl_copy_header_value(const char *header)
/* get length of the type */
len = end - start + 1;
- value = malloc(len + 1);
- if(!value)
- return NULL;
-
- memcpy(value, start, len);
- value[len] = 0; /* null-terminate */
-
- return value;
+ return Curl_memdup0(start, len);
}
#ifndef CURL_DISABLE_HTTP_AUTH
@@ -1597,9 +1517,9 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done)
/* this returns the socket to wait for in the DO and DOING state for the multi
interface and then we're always _sending_ a request and thus we wait for
the single socket to become writable only */
-static int http_getsock_do(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks)
+int Curl_http_getsock_do(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
{
/* write mode */
(void)conn;
@@ -2103,6 +2023,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
switch(data->set.timecondition) {
default:
+ DEBUGF(infof(data, "invalid time condition"));
return CURLE_BAD_FUNCTION_ARGUMENT;
case CURL_TIMECOND_IFMODSINCE:
@@ -2271,7 +2192,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
}
#endif
- if(strcmp("Host:", ptr)) {
+ if(!strcasecompare("Host:", ptr)) {
aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
if(!aptr->host)
return CURLE_OUT_OF_MEMORY;
@@ -2359,9 +2280,7 @@ CURLcode Curl_http_target(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
}
- /* Extract the URL to use in the request. Store in STRING_TEMP_URL for
- clean-up reasons if the function returns before the free() further
- down. */
+ /* Extract the URL to use in the request. */
uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT);
if(uc) {
curl_url_cleanup(h);
@@ -3021,13 +2940,14 @@ CURLcode Curl_http_resume(struct Curl_easy *data,
}
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
+ (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) :
curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.fread_func(scratch, 1, readthisamountnow,
data->state.in);
passed += actuallyread;
@@ -3062,6 +2982,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
{
struct SingleRequest *k = &data->req;
+ *done = FALSE;
if(data->req.newurl) {
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
@@ -3187,7 +3108,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
) {
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
- return result;
+ goto fail;
}
else
#endif
@@ -3202,7 +3123,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
- return result;
+ goto fail;
}
break;
}
@@ -3212,11 +3133,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_http_host(data, conn);
if(result)
- return result;
+ goto fail;
result = Curl_http_useragent(data);
if(result)
- return result;
+ goto fail;
Curl_http_method(data, conn, &request, &httpreq);
@@ -3232,7 +3153,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
(pq ? pq : data->state.up.path), FALSE);
free(pq);
if(result)
- return result;
+ goto fail;
}
Curl_safefree(data->state.aptr.ref);
@@ -3257,23 +3178,23 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
/* we only consider transfer-encoding magic if libz support is built-in */
result = Curl_transferencode(data);
if(result)
- return result;
+ goto fail;
#endif
result = Curl_http_body(data, conn, httpreq, &te);
if(result)
- return result;
+ goto fail;
p_accept = Curl_checkheaders(data,
STRCONST("Accept"))?NULL:"Accept: */*\r\n";
result = Curl_http_resume(data, conn, httpreq);
if(result)
- return result;
+ goto fail;
result = Curl_http_range(data, httpreq);
if(result)
- return result;
+ goto fail;
httpstring = get_http_string(data, conn);
@@ -3291,7 +3212,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_http_target(data, conn, &req);
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
#ifndef CURL_DISABLE_ALTSVC
@@ -3362,7 +3283,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
@@ -3398,7 +3319,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
if((http->postsize > -1) &&
@@ -3434,6 +3355,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
data->req.upload_chunky = FALSE;
+fail:
+ if(CURLE_TOO_LARGE == result)
+ failf(data, "HTTP request too large");
return result;
}
@@ -3896,7 +3820,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
* fields. */
if(data->set.timecondition)
data->info.timecond = TRUE;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 204:
/* (quote from RFC2616, section 10.2.5): The server has
* fulfilled the request but does not need to return an
@@ -3995,15 +3919,16 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data,
/*
* Read any HTTP header lines from the server and pass them to the client app.
*/
-CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
- struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed)
+static CURLcode http_rw_headers(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed)
{
- CURLcode result;
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
char *headp;
char *end_ptr;
+ bool leftover_body = FALSE;
/* header line within buffer loop */
*pconsumed = 0;
@@ -4032,12 +3957,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(st == STATUS_BAD) {
/* this is not the beginning of a protocol first header line */
k->header = FALSE;
- k->badheader = TRUE;
streamclose(conn, "bad HTTP: No end-of-message indicator");
if(!data->set.http09_allowed) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
+ leftover_body = TRUE;
goto out;
}
}
@@ -4071,15 +3996,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
return CURLE_UNSUPPORTED_PROTOCOL;
}
k->header = FALSE;
- if(blen)
- /* since there's more, this is a partial bad header */
- k->badheader = TRUE;
- else {
- /* this was all we read so it's all a bad header */
- k->badheader = TRUE;
- return CURLE_OK;
- }
- break;
+ leftover_body = TRUE;
+ goto out;
}
}
@@ -4088,6 +4006,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
headp = Curl_dyn_ptr(&data->state.headerb);
if((0x0a == *headp) || (0x0d == *headp)) {
size_t headerlen;
+ bool switch_to_h2 = FALSE;
/* Zero-length header line means end of headers! */
if('\r' == *headp)
@@ -4117,42 +4036,40 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
break;
case 101:
- /* 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;
-
- /* we'll get more headers (HTTP/2 response) */
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
-
- /* switch to http2 now. The bytes after response headers
- are also processed here, otherwise they are lost. */
- result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
- if(result)
- return result;
- *pconsumed += blen;
- blen = 0;
- }
+ if(conn->httpversion == 11) {
+ /* Switching Protocols only allowed from HTTP/1.1 */
+ if(k->upgr101 == UPGR101_H2) {
+ /* Switching to HTTP/2 */
+ infof(data, "Received 101, Switching to HTTP/2");
+ k->upgr101 = UPGR101_RECEIVED;
+
+ /* we'll get more headers (HTTP/2 response) */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+ switch_to_h2 = TRUE;
+ }
#ifdef USE_WEBSOCKETS
- else if(k->upgr101 == UPGR101_WS) {
- /* verify the response */
- result = Curl_ws_accept(data, buf, blen);
- if(result)
- return result;
- k->header = FALSE; /* no more header to parse! */
- if(data->set.connect_only) {
- k->keepon &= ~KEEP_RECV; /* read no more content */
- *pconsumed += blen;
+ else if(k->upgr101 == UPGR101_WS) {
+ /* verify the response */
+ result = Curl_ws_accept(data, buf, blen);
+ if(result)
+ return result;
+ k->header = FALSE; /* no more header to parse! */
+ *pconsumed += blen; /* ws accept handled the data */
blen = 0;
+ if(data->set.connect_only)
+ k->keepon &= ~KEEP_RECV; /* read no more content */
}
- }
#endif
+ else {
+ /* Not switching to another protocol */
+ k->header = FALSE; /* no more header to parse! */
+ }
+ }
else {
- /* Not switching to another protocol */
- k->header = FALSE; /* no more header to parse! */
+ /* invalid for other HTTP versions */
+ failf(data, "unexpected 101 response code");
+ return CURLE_WEIRD_SERVER_REPLY;
}
break;
default:
@@ -4359,16 +4276,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
*/
if(data->req.no_body)
k->download_done = TRUE;
-#ifndef CURL_DISABLE_RTSP
- else if((conn->handler->protocol & CURLPROTO_RTSP) &&
- (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
- (k->size <= -1))
- /* Respect section 4.4 of rfc2326: If the Content-Length header is
- absent, a length 0 must be assumed. It will prevent libcurl from
- hanging on DESCRIBE request that got refused for whatever
- reason */
- k->download_done = TRUE;
-#endif
/* If max download size is *zero* (nothing) we already have
nothing and can safely return ok now! But for HTTP/2, we'd
@@ -4388,6 +4295,17 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* We continue reading headers, reset the line-based header */
Curl_dyn_reset(&data->state.headerb);
+ if(switch_to_h2) {
+ /* Having handled the headers, we can do the HTTP/2 switch.
+ * Any remaining `buf` bytes are already HTTP/2 and passed to
+ * be processed. */
+ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+ if(result)
+ return result;
+ *pconsumed += blen;
+ blen = 0;
+ }
+
continue;
}
@@ -4578,9 +4496,78 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
there might be a non-header part left in the end of the read
buffer. */
out:
+ if(!k->header && !leftover_body) {
+ Curl_dyn_free(&data->state.headerb);
+ }
return CURLE_OK;
}
+/*
+ * HTTP protocol `write_resp` implementation. Will parse headers
+ * when not done yet and otherwise return without consuming data.
+ */
+CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed,
+ bool *done)
+{
+ *done = FALSE;
+ if(!data->req.header) {
+ *pconsumed = 0;
+ return CURLE_OK;
+ }
+ else {
+ CURLcode result;
+
+ result = http_rw_headers(data, buf, blen, pconsumed);
+ if(!result && !data->req.header) {
+ /* we have successfully finished parsing the HEADERs */
+ result = Curl_http_firstwrite(data, data->conn, done);
+
+ if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+ /* leftover from parsing something that turned out not
+ * to be a header, only happens if we allow for
+ * HTTP/0.9 like responses */
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ }
+ Curl_dyn_free(&data->state.headerb);
+ }
+ return result;
+ }
+}
+
+CURLcode Curl_http_write_resp(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ bool is_eos,
+ bool *done)
+{
+ CURLcode result;
+ size_t consumed;
+ int flags;
+
+ *done = FALSE;
+ result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
+ if(result || *done)
+ goto out;
+
+ DEBUGASSERT(consumed <= blen);
+ blen -= consumed;
+ buf += consumed;
+ /* either all was consumed in header parsing, or we have data left
+ * and are done with heders, e.g. it is BODY data */
+ DEBUGASSERT(!blen || !data->req.header);
+ if(!data->req.header && (blen || is_eos)) {
+ /* BODY data after header been parsed, write and consume */
+ flags = CLIENTWRITE_BODY;
+ if(is_eos)
+ flags |= CLIENTWRITE_EOS;
+ result = Curl_client_write(data, flags, (char *)buf, blen);
+ }
+out:
+ return result;
+}
/* Decode HTTP status code string. */
CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len)
@@ -4617,7 +4604,7 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
CURLcode result = CURLE_OUT_OF_MEMORY;
DEBUGASSERT(method);
- if(m_len + 1 >= sizeof(req->method))
+ if(m_len + 1 > sizeof(req->method))
return CURLE_BAD_FUNCTION_ARGUMENT;
req = calloc(1, sizeof(*req));
@@ -4625,17 +4612,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
goto out;
memcpy(req->method, method, m_len);
if(scheme) {
- req->scheme = Curl_strndup(scheme, s_len);
+ req->scheme = Curl_memdup0(scheme, s_len);
if(!req->scheme)
goto out;
}
if(authority) {
- req->authority = Curl_strndup(authority, a_len);
+ req->authority = Curl_memdup0(authority, a_len);
if(!req->authority)
goto out;
}
if(path) {
- req->path = Curl_strndup(path, p_len);
+ req->path = Curl_memdup0(path, p_len);
if(!req->path)
goto out;
}
@@ -4773,7 +4760,7 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
CURLUcode uc;
DEBUGASSERT(method);
- if(m_len + 1 >= sizeof(req->method))
+ if(m_len + 1 > sizeof(req->method))
return CURLE_BAD_FUNCTION_ARGUMENT;
req = calloc(1, sizeof(*req));
diff --git a/libs/libcurl/src/http.h b/libs/libcurl/src/http.h
index f7571ebdb0..3ceb049693 100644
--- a/libs/libcurl/src/http.h
+++ b/libs/libcurl/src/http.h
@@ -54,14 +54,6 @@ extern const struct Curl_handler Curl_handler_http;
extern const struct Curl_handler Curl_handler_https;
#endif
-#ifdef USE_WEBSOCKETS
-extern const struct Curl_handler Curl_handler_ws;
-
-#ifdef USE_SSL
-extern const struct Curl_handler Curl_handler_wss;
-#endif
-#endif /* websockets */
-
struct dynhds;
CURLcode Curl_bump_headersize(struct Curl_easy *data,
@@ -147,9 +139,17 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
bool *done);
/* protocol-specific functions set up to be called by the main engine */
+CURLcode Curl_http_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn);
CURLcode Curl_http(struct Curl_easy *data, bool *done);
CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature);
CURLcode Curl_http_connect(struct Curl_easy *data, bool *done);
+int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks);
+CURLcode Curl_http_write_resp(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ bool is_eos,
+ bool *done);
/* These functions are in http.c */
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
@@ -225,10 +225,10 @@ struct HTTP {
CURLcode Curl_http_size(struct Curl_easy *data);
-CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
- struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed);
+CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed,
+ bool *done);
/**
* Curl_http_output_auth() setups the authentication headers for the
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index 6a7d18379d..fbec0a7927 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -219,10 +219,10 @@ static void drain_stream(struct Curl_cfilter *cf,
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",
+ if(data->state.select_bits != bits) {
+ CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
stream->id, bits);
- data->state.dselect_bits = bits;
+ data->state.select_bits = bits;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
@@ -283,13 +283,20 @@ static void http2_data_done(struct Curl_cfilter *cf,
return;
if(ctx->h2) {
+ bool flush_egress = FALSE;
+ /* returns error if stream not known, which is fine here */
+ (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
+
if(!stream->closed && stream->id > 0) {
/* RST_STREAM */
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);
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ stream->send_closed = TRUE;
+ nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, NGHTTP2_STREAM_CLOSED);
+ flush_egress = TRUE;
}
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
/* Anything in the recvbuf is still being counted
@@ -299,19 +306,11 @@ static void http2_data_done(struct Curl_cfilter *cf,
nghttp2_session_consume(ctx->h2, stream->id,
Curl_bufq_len(&stream->recvbuf));
/* give WINDOW_UPATE a chance to be sent, but ignore any error */
- (void)h2_progress_egress(cf, data);
+ flush_egress = TRUE;
}
- /* -1 means unassigned and 0 means cleared */
- if(nghttp2_session_get_stream_user_data(ctx->h2, stream->id)) {
- int rv = nghttp2_session_set_stream_user_data(ctx->h2,
- stream->id, 0);
- if(rv) {
- infof(data, "http/2: failed to clear user_data for stream %u",
- stream->id);
- DEBUGASSERT(0);
- }
- }
+ if(flush_egress)
+ nghttp2_session_send(ctx->h2);
}
Curl_bufq_free(&stream->sendbuf);
@@ -1316,26 +1315,43 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *userp)
{
struct Curl_cfilter *cf = userp;
- struct Curl_easy *data_s;
+ struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
struct stream_ctx *stream;
int rv;
(void)session;
+ DEBUGASSERT(call_data);
/* get the stream from the hash based on Stream ID, stream ID zero is for
connection-oriented stuff */
data_s = stream_id?
nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
if(!data_s) {
+ CURL_TRC_CF(call_data, cf,
+ "[%d] on_stream_close, no easy set on stream", stream_id);
return 0;
}
+ if(!GOOD_EASY_HANDLE(data_s)) {
+ /* nghttp2 still has an easy registered for the stream which has
+ * been freed be libcurl. This points to a code path that does not
+ * trigger DONE or DETACH events as it must. */
+ CURL_TRC_CF(call_data, cf,
+ "[%d] on_stream_close, not a GOOD easy on stream", stream_id);
+ (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
stream = H2_STREAM_CTX(data_s);
- if(!stream)
+ if(!stream) {
+ CURL_TRC_CF(data_s, cf,
+ "[%d] on_stream_close, GOOD easy but no stream", stream_id);
return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
stream->closed = TRUE;
stream->error = error_code;
- if(stream->error)
+ if(stream->error) {
stream->reset = TRUE;
+ stream->send_closed = TRUE;
+ }
if(stream->error)
CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
@@ -2315,18 +2331,22 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
struct easy_pollset *ps)
{
struct cf_h2_ctx *ctx = cf->ctx;
- bool want_recv = CURL_WANT_RECV(data);
- bool want_send = CURL_WANT_SEND(data);
+ curl_socket_t sock;
+ bool want_recv, want_send;
+
+ if(!ctx->h2)
+ return;
- if(ctx->h2 && (want_recv || want_send)) {
+ sock = Curl_conn_cf_get_socket(cf, data);
+ Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
+ if(want_recv || want_send) {
struct stream_ctx *stream = H2_STREAM_CTX(data);
- curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
struct cf_call_data save;
bool c_exhaust, s_exhaust;
CF_DATA_SAVE(save, cf, data);
- c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2);
- s_exhaust = stream && stream->id >= 0 &&
+ c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
+ s_exhaust = want_send && stream && stream->id >= 0 &&
!nghttp2_session_get_stream_remote_window_size(ctx->h2,
stream->id);
want_recv = (want_recv || c_exhaust || s_exhaust);
diff --git a/libs/libcurl/src/http_aws_sigv4.c b/libs/libcurl/src/http_aws_sigv4.c
index 0313026562..deea1d4b00 100644
--- a/libs/libcurl/src/http_aws_sigv4.c
+++ b/libs/libcurl/src/http_aws_sigv4.c
@@ -247,7 +247,7 @@ static CURLcode make_headers(struct Curl_easy *data,
}
else {
char *value;
-
+ char *endp;
value = strchr(*date_header, ':');
if(!value) {
*date_header = NULL;
@@ -256,8 +256,17 @@ static CURLcode make_headers(struct Curl_easy *data,
++value;
while(ISBLANK(*value))
++value;
- strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
- timestamp[TIMESTAMP_SIZE - 1] = 0;
+ endp = value;
+ while(*endp && ISALNUM(*endp))
+ ++endp;
+ /* 16 bytes => "19700101T000000Z" */
+ if((endp - value) == TIMESTAMP_SIZE - 1) {
+ memcpy(timestamp, value, TIMESTAMP_SIZE - 1);
+ timestamp[TIMESTAMP_SIZE - 1] = 0;
+ }
+ else
+ /* bad timestamp length */
+ timestamp[0] = 0;
*date_header = NULL;
}
@@ -605,7 +614,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
result = CURLE_URL_MALFORMAT;
goto fail;
}
- strncpy(service, hostname, len);
+ memcpy(service, hostname, len);
service[len] = '\0';
infof(data, "aws_sigv4: picked service %s from host", service);
@@ -624,7 +633,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
result = CURLE_URL_MALFORMAT;
goto fail;
}
- strncpy(region, reg, len);
+ memcpy(region, reg, len);
region[len] = '\0';
infof(data, "aws_sigv4: picked region %s from host", region);
}
diff --git a/libs/libcurl/src/http_chunks.c b/libs/libcurl/src/http_chunks.c
index 0d6d6302a4..8f8098e015 100644
--- a/libs/libcurl/src/http_chunks.c
+++ b/libs/libcurl/src/http_chunks.c
@@ -75,47 +75,67 @@
*/
-void Curl_httpchunk_init(struct Curl_easy *data)
+void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
+ bool ignore_body)
{
- struct connectdata *conn = data->conn;
- struct Curl_chunker *chunk = &conn->chunk;
- chunk->hexindex = 0; /* start at 0 */
- chunk->state = CHUNK_HEX; /* we get hex first! */
- Curl_dyn_init(&conn->trailer, DYN_H1_TRAILER);
+ (void)data;
+ ch->hexindex = 0; /* start at 0 */
+ ch->state = CHUNK_HEX; /* we get hex first! */
+ ch->last_code = CHUNKE_OK;
+ Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
+ ch->ignore_body = ignore_body;
}
-/*
- * chunk_read() returns a OK for normal operations, or a positive return code
- * for errors. STOP means this sequence of chunks is complete. The 'wrote'
- * argument is set to tell the caller how many bytes we actually passed to the
- * client (for byte-counting and whatever).
- *
- * The states and the state-machine is further explained in the header file.
- *
- * This function always uses ASCII hex values to accommodate non-ASCII hosts.
- * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
- */
-CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
- char *buf,
- size_t blen,
- size_t *pconsumed,
- CURLcode *extrap)
+void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
+ bool ignore_body)
+{
+ (void)data;
+ ch->hexindex = 0; /* start at 0 */
+ ch->state = CHUNK_HEX; /* we get hex first! */
+ ch->last_code = CHUNKE_OK;
+ Curl_dyn_reset(&ch->trailer);
+ ch->ignore_body = ignore_body;
+}
+
+void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
+{
+ (void)data;
+ Curl_dyn_free(&ch->trailer);
+}
+
+bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
+{
+ (void)data;
+ return ch->state == CHUNK_DONE;
+}
+
+static CURLcode httpchunk_readwrite(struct Curl_easy *data,
+ struct Curl_chunker *ch,
+ struct Curl_cwriter *cw_next,
+ const char *buf, size_t blen,
+ size_t *pconsumed)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct Curl_chunker *ch = &conn->chunk;
- struct SingleRequest *k = &data->req;
size_t piece;
*pconsumed = 0; /* nothing's written yet */
+ /* first check terminal states that will not progress anywhere */
+ if(ch->state == CHUNK_DONE)
+ return CURLE_OK;
+ if(ch->state == CHUNK_FAILED)
+ return CURLE_RECV_ERROR;
/* the original data is written to the client, but we go on with the
chunk read process, to properly calculate the content length */
- if(data->set.http_te_skip && !k->ignorebody) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
+ if(data->set.http_te_skip && !ch->ignore_body) {
+ if(cw_next)
+ result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
if(result) {
- *extrap = result;
- return CHUNKE_PASSTHRU_ERROR;
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_PASSTHRU_ERROR;
+ return result;
}
}
@@ -123,28 +143,35 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
switch(ch->state) {
case CHUNK_HEX:
if(ISXDIGIT(*buf)) {
- if(ch->hexindex < CHUNK_MAXNUM_LEN) {
- ch->hexbuffer[ch->hexindex] = *buf;
- buf++;
- blen--;
- ch->hexindex++;
- }
- else {
- return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
+ if(ch->hexindex >= CHUNK_MAXNUM_LEN) {
+ failf(data, "chunk hex-length longer than %d", CHUNK_MAXNUM_LEN);
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_TOO_LONG_HEX; /* longer than we support */
+ return CURLE_RECV_ERROR;
}
+ ch->hexbuffer[ch->hexindex++] = *buf;
+ buf++;
+ blen--;
}
else {
char *endptr;
- if(0 == ch->hexindex)
+ if(0 == ch->hexindex) {
/* This is illegal data, we received junk where we expected
a hexadecimal digit. */
- return CHUNKE_ILLEGAL_HEX;
+ failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf);
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_ILLEGAL_HEX;
+ return CURLE_RECV_ERROR;
+ }
/* blen and buf are unmodified */
ch->hexbuffer[ch->hexindex] = 0;
-
- if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
- return CHUNKE_ILLEGAL_HEX;
+ if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) {
+ failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_ILLEGAL_HEX;
+ return CURLE_RECV_ERROR;
+ }
ch->state = CHUNK_LF; /* now wait for the CRLF */
}
break;
@@ -173,12 +200,17 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
piece = curlx_sotouz(ch->datasize);
/* Write the data portion available */
- if(!data->set.http_te_skip && !k->ignorebody) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece);
-
+ if(!data->set.http_te_skip && !ch->ignore_body) {
+ if(cw_next)
+ result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
+ buf, piece);
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ (char *)buf, piece);
if(result) {
- *extrap = result;
- return CHUNKE_PASSTHRU_ERROR;
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_PASSTHRU_ERROR;
+ return result;
}
}
@@ -195,38 +227,51 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
case CHUNK_POSTLF:
if(*buf == 0x0a) {
/* The last one before we go back to hex state and start all over. */
- Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */
+ Curl_httpchunk_reset(data, ch, ch->ignore_body);
+ }
+ else if(*buf != 0x0d) {
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_BAD_CHUNK;
+ return CURLE_RECV_ERROR;
}
- else if(*buf != 0x0d)
- return CHUNKE_BAD_CHUNK;
buf++;
blen--;
break;
case CHUNK_TRAILER:
if((*buf == 0x0d) || (*buf == 0x0a)) {
- char *tr = Curl_dyn_ptr(&conn->trailer);
+ char *tr = Curl_dyn_ptr(&ch->trailer);
/* this is the end of a trailer, but if the trailer was zero bytes
there was no trailer and we move on */
if(tr) {
size_t trlen;
- result = Curl_dyn_addn(&conn->trailer, (char *)STRCONST("\x0d\x0a"));
- if(result)
- return CHUNKE_OUT_OF_MEMORY;
-
- tr = Curl_dyn_ptr(&conn->trailer);
- trlen = Curl_dyn_len(&conn->trailer);
+ result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
+ if(result) {
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_OUT_OF_MEMORY;
+ return result;
+ }
+ tr = Curl_dyn_ptr(&ch->trailer);
+ trlen = Curl_dyn_len(&ch->trailer);
if(!data->set.http_te_skip) {
- result = Curl_client_write(data,
- CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
- tr, trlen);
+ if(cw_next)
+ result = Curl_cwriter_write(data, cw_next,
+ CLIENTWRITE_HEADER|
+ CLIENTWRITE_TRAILER,
+ tr, trlen);
+ else
+ result = Curl_client_write(data,
+ CLIENTWRITE_HEADER|
+ CLIENTWRITE_TRAILER,
+ tr, trlen);
if(result) {
- *extrap = result;
- return CHUNKE_PASSTHRU_ERROR;
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_PASSTHRU_ERROR;
+ return result;
}
}
- Curl_dyn_reset(&conn->trailer);
+ Curl_dyn_reset(&ch->trailer);
ch->state = CHUNK_TRAILER_CR;
if(*buf == 0x0a)
/* already on the LF */
@@ -239,9 +284,12 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
}
}
else {
- result = Curl_dyn_addn(&conn->trailer, buf, 1);
- if(result)
- return CHUNKE_OUT_OF_MEMORY;
+ result = Curl_dyn_addn(&ch->trailer, buf, 1);
+ if(result) {
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_OUT_OF_MEMORY;
+ return result;
+ }
}
buf++;
blen--;
@@ -253,8 +301,11 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
buf++;
blen--;
}
- else
- return CHUNKE_BAD_CHUNK;
+ else {
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_BAD_CHUNK;
+ return CURLE_RECV_ERROR;
+ }
break;
case CHUNK_TRAILER_POSTCR:
@@ -277,21 +328,29 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
case CHUNK_STOP:
if(*buf == 0x0a) {
blen--;
-
/* Record the length of any data left in the end of the buffer
even if there's no more chunks to read */
ch->datasize = blen;
-
- return CHUNKE_STOP; /* return stop */
+ ch->state = CHUNK_DONE;
+ return CURLE_OK;
}
- else
- return CHUNKE_BAD_CHUNK;
+ else {
+ ch->state = CHUNK_FAILED;
+ ch->last_code = CHUNKE_BAD_CHUNK;
+ return CURLE_RECV_ERROR;
+ }
+ case CHUNK_DONE:
+ return CURLE_OK;
+
+ case CHUNK_FAILED:
+ return CURLE_RECV_ERROR;
}
+
}
- return CHUNKE_OK;
+ return CURLE_OK;
}
-const char *Curl_chunked_strerror(CHUNKcode code)
+static const char *Curl_chunked_strerror(CHUNKcode code)
{
switch(code) {
default:
@@ -303,8 +362,7 @@ const char *Curl_chunked_strerror(CHUNKcode code)
case CHUNKE_BAD_CHUNK:
return "Malformed encoding found";
case CHUNKE_PASSTHRU_ERROR:
- DEBUGASSERT(0); /* never used */
- return "";
+ return "Error writing data to client";
case CHUNKE_BAD_ENCODING:
return "Bad content-encoding found";
case CHUNKE_OUT_OF_MEMORY:
@@ -312,4 +370,86 @@ const char *Curl_chunked_strerror(CHUNKcode code)
}
}
+CURLcode Curl_httpchunk_read(struct Curl_easy *data,
+ struct Curl_chunker *ch,
+ char *buf, size_t blen,
+ size_t *pconsumed)
+{
+ return httpchunk_readwrite(data, ch, NULL, buf, blen, pconsumed);
+}
+
+struct chunked_writer {
+ struct Curl_cwriter super;
+ struct Curl_chunker ch;
+};
+
+static CURLcode cw_chunked_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
+{
+ struct chunked_writer *ctx = (struct chunked_writer *)writer;
+
+ data->req.chunk = TRUE; /* chunks coming our way. */
+ Curl_httpchunk_init(data, &ctx->ch, FALSE);
+ return CURLE_OK;
+}
+
+static void cw_chunked_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
+{
+ struct chunked_writer *ctx = (struct chunked_writer *)writer;
+ Curl_httpchunk_free(data, &ctx->ch);
+}
+
+static CURLcode cw_chunked_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t blen)
+{
+ struct chunked_writer *ctx = (struct chunked_writer *)writer;
+ CURLcode result;
+ size_t consumed;
+
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, blen);
+
+ consumed = 0;
+ result = httpchunk_readwrite(data, &ctx->ch, writer->next, buf, blen,
+ &consumed);
+
+ if(result) {
+ if(CHUNKE_PASSTHRU_ERROR == ctx->ch.last_code) {
+ failf(data, "Failed reading the chunked-encoded stream");
+ }
+ else {
+ failf(data, "%s in chunked-encoding",
+ Curl_chunked_strerror(ctx->ch.last_code));
+ }
+ return result;
+ }
+
+ blen -= consumed;
+ if(CHUNK_DONE == ctx->ch.state) {
+ /* chunks read successfully, download is complete */
+ data->req.download_done = TRUE;
+ if(blen) {
+ infof(data, "Leftovers after chunking: %zu bytes", blen);
+ }
+ }
+ else if((type & CLIENTWRITE_EOS) && !data->req.no_body) {
+ failf(data, "transfer closed with outstanding read data remaining");
+ return CURLE_PARTIAL_FILE;
+ }
+
+ return CURLE_OK;
+}
+
+/* HTTP chunked Transfer-Encoding decoder */
+const struct Curl_cwtype Curl_httpchunk_unencoder = {
+ "chunked",
+ NULL,
+ cw_chunked_init,
+ cw_chunked_write,
+ cw_chunked_close,
+ sizeof(struct chunked_writer)
+};
+
#endif /* CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/http_chunks.h b/libs/libcurl/src/http_chunks.h
index 3c228f84eb..ea89949d7f 100644
--- a/libs/libcurl/src/http_chunks.h
+++ b/libs/libcurl/src/http_chunks.h
@@ -24,6 +24,10 @@
*
***************************************************************************/
+#ifndef CURL_DISABLE_HTTP
+
+#include "dynbuf.h"
+
struct connectdata;
/*
@@ -67,34 +71,68 @@ typedef enum {
signalled If this is an empty trailer CHUNKE_STOP will be signalled.
Otherwise the trailer will be broadcasted via Curl_client_write() and the
next state will be CHUNK_TRAILER */
- CHUNK_TRAILER_POSTCR
+ CHUNK_TRAILER_POSTCR,
+
+ /* Successfully de-chunked everything */
+ CHUNK_DONE,
+
+ /* Failed on seeing a bad or not correctly terminated chunk */
+ CHUNK_FAILED
} ChunkyState;
typedef enum {
- CHUNKE_STOP = -1,
CHUNKE_OK = 0,
CHUNKE_TOO_LONG_HEX = 1,
CHUNKE_ILLEGAL_HEX,
CHUNKE_BAD_CHUNK,
CHUNKE_BAD_ENCODING,
CHUNKE_OUT_OF_MEMORY,
- CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */
- CHUNKE_LAST
+ CHUNKE_PASSTHRU_ERROR /* Curl_httpchunk_read() returns a CURLcode to use */
} CHUNKcode;
-const char *Curl_chunked_strerror(CHUNKcode code);
-
struct Curl_chunker {
curl_off_t datasize;
ChunkyState state;
+ CHUNKcode last_code;
+ struct dynbuf trailer; /* for chunked-encoded trailer */
unsigned char hexindex;
- char hexbuffer[ CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */
+ char hexbuffer[CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */
+ BIT(ignore_body); /* never write response body data */
};
/* The following functions are defined in http_chunks.c */
-void Curl_httpchunk_init(struct Curl_easy *data);
-CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *buf,
- size_t blen, size_t *pconsumed,
- CURLcode *passthru);
+void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
+ bool ignore_body);
+void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch);
+void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
+ bool ignore_body);
+
+/*
+ * Read BODY bytes in HTTP/1.1 chunked encoding from `buf` and return
+ * the amount of bytes consumed. The actual response bytes and trailer
+ * headers are written out to the client.
+ * On success, this will consume all bytes up to the end of the response,
+ * e.g. the last chunk, has been processed.
+ * @param data the transfer involved
+ * @param ch the chunker instance keeping state across calls
+ * @param buf the response data
+ * @param blen amount of bytes in `buf`
+ * @param pconsumed on successful return, the number of bytes in `buf`
+ * consumed
+ *
+ * This function always uses ASCII hex values to accommodate non-ASCII hosts.
+ * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
+ */
+CURLcode Curl_httpchunk_read(struct Curl_easy *data, struct Curl_chunker *ch,
+ char *buf, size_t blen, size_t *pconsumed);
+
+/**
+ * @return TRUE iff chunked decoded has finished successfully.
+ */
+bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch);
+
+extern const struct Curl_cwtype Curl_httpchunk_unencoder;
+
+#endif /* !CURL_DISABLE_HTTP */
#endif /* HEADER_CURL_HTTP_CHUNKS_H */
diff --git a/libs/libcurl/src/http_proxy.c b/libs/libcurl/src/http_proxy.c
index 56bf385e70..bf0f3ff87d 100644
--- a/libs/libcurl/src/http_proxy.c
+++ b/libs/libcurl/src/http_proxy.c
@@ -131,8 +131,8 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
goto out;
}
- if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent"))
- && data->set.str[STRING_USERAGENT]) {
+ if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
+ data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
result = Curl_dynhds_cadd(&req->headers, "User-Agent",
data->set.str[STRING_USERAGENT]);
if(result)
diff --git a/libs/libcurl/src/imap.c b/libs/libcurl/src/imap.c
index d4abe3568c..0cec828c46 100644
--- a/libs/libcurl/src/imap.c
+++ b/libs/libcurl/src/imap.c
@@ -97,7 +97,8 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
static CURLcode imap_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static char *imap_atom(const char *str, bool escape_only);
-static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...);
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
+ CURL_PRINTF(2, 3);
static CURLcode imap_parse_url_options(struct connectdata *conn);
static CURLcode imap_parse_url_path(struct Curl_easy *data);
static CURLcode imap_parse_custom_request(struct Curl_easy *data);
@@ -129,7 +130,7 @@ const struct Curl_handler Curl_handler_imap = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
imap_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_IMAP, /* defport */
@@ -158,7 +159,7 @@ const struct Curl_handler Curl_handler_imaps = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
imap_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_IMAPS, /* defport */
@@ -354,8 +355,8 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = data->state.buffer;
- size_t len = strlen(message);
+ char *message = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+ size_t len = data->conn->proto.imapc.pp.nfinal;
if(len > 2) {
/* Find the start of the message */
@@ -895,7 +896,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct imap_conn *imapc = &conn->proto.imapc;
- const char *line = data->state.buffer;
+ const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf);
(void)instate; /* no use for this yet */
@@ -981,7 +982,7 @@ static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
(void)instate; /* no use for this yet */
/* Pipelining in response is forbidden. */
- if(data->conn->proto.imapc.pp.cache_size)
+ if(data->conn->proto.imapc.pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(imapcode != IMAP_RESP_OK) {
@@ -1057,17 +1058,13 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
imapstate instate)
{
CURLcode result = CURLE_OK;
- char *line = data->state.buffer;
- size_t len = strlen(line);
+ char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+ size_t len = data->conn->proto.imapc.pp.nfinal;
(void)instate; /* No use for this yet */
- if(imapcode == '*') {
- /* Temporarily add the LF character back and send as body to the client */
- line[len] = '\n';
- result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
- line[len] = '\0';
- }
+ if(imapcode == '*')
+ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
else if(imapcode != IMAP_RESP_OK)
result = CURLE_QUOTE_ERROR;
else
@@ -1085,7 +1082,7 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
struct connectdata *conn = data->conn;
struct IMAP *imap = data->req.p.imap;
struct imap_conn *imapc = &conn->proto.imapc;
- const char *line = data->state.buffer;
+ const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
(void)instate; /* no use for this yet */
@@ -1144,7 +1141,8 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct imap_conn *imapc = &conn->proto.imapc;
struct pingpong *pp = &imapc->pp;
- const char *ptr = data->state.buffer;
+ const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+ size_t len = data->conn->proto.imapc.pp.nfinal;
bool parsed = FALSE;
curl_off_t size = 0;
@@ -1158,16 +1156,12 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
/* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
the continuation data contained within the curly brackets */
- while(*ptr && (*ptr != '{'))
- ptr++;
-
- if(*ptr == '{') {
+ ptr = memchr(ptr, '{', len);
+ if(ptr) {
char *endptr;
- if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
- if(endptr - ptr > 1 && endptr[0] == '}' &&
- endptr[1] == '\r' && endptr[2] == '\0')
- parsed = TRUE;
- }
+ if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) &&
+ (endptr - ptr > 1 && *endptr == '}'))
+ parsed = TRUE;
}
if(parsed) {
@@ -1175,11 +1169,15 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
size);
Curl_pgrsSetDownloadSize(data, size);
- if(pp->cache) {
- /* At this point there is a bunch of data in the header "cache" that is
- actually body content, send it as body and then skip it. Do note
- that there may even be additional "headers" after the body. */
- size_t chunk = pp->cache_size;
+ if(pp->overflow) {
+ /* At this point there is a data in the receive buffer that is body
+ content, send it as body and then skip it. Do note that there may
+ even be additional "headers" after the body. */
+ size_t chunk = pp->overflow;
+
+ /* keep only the overflow */
+ Curl_dyn_tail(&pp->recvbuf, chunk);
+ pp->nfinal = 0; /* done */
if(chunk > (size_t)size)
/* The conversion from curl_off_t to size_t is always fine here */
@@ -1190,25 +1188,24 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
imap_state(data, IMAP_STOP);
return CURLE_OK;
}
- result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&pp->recvbuf), chunk);
if(result)
return result;
infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
" bytes are left for transfer", chunk, size - chunk);
- /* Have we used the entire cache or just part of it?*/
- if(pp->cache_size > chunk) {
- /* Only part of it so shrink the cache to fit the trailing data */
- memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
- pp->cache_size -= chunk;
+ /* Have we used the entire overflow or just part of it?*/
+ if(pp->overflow > chunk) {
+ /* remember the remaining trailing overflow data */
+ pp->overflow -= chunk;
+ Curl_dyn_tail(&pp->recvbuf, pp->overflow);
}
else {
+ pp->overflow = 0; /* handled */
/* Free the cache */
- Curl_safefree(pp->cache);
-
- /* Reset the cache size */
- pp->cache_size = 0;
+ Curl_dyn_reset(&pp->recvbuf);
}
}
@@ -1220,7 +1217,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
data->req.maxdownload = size;
/* force a recv/send check of this connection, as the data might've been
read off the socket already */
- data->conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
}
}
@@ -1376,7 +1373,6 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
break;
case IMAP_LOGOUT:
- /* fallthrough, just stop! */
default:
/* internal error */
imap_state(data, IMAP_STOP);
@@ -1472,9 +1468,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done)
Curl_sasl_init(&imapc->sasl, data, &saslimap);
Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
- /* Initialise the pingpong layer */
- Curl_pp_setup(pp);
- Curl_pp_init(data, pp);
+ Curl_pp_init(pp);
/* Parse the URL options */
result = imap_parse_url_options(conn);
@@ -1795,7 +1789,14 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
if(!result) {
va_list ap;
va_start(ap, fmt);
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
va_end(ap);
}
return result;
diff --git a/libs/libcurl/src/inet_pton.c b/libs/libcurl/src/inet_pton.c
index a21679297f..04ccd2e97d 100644
--- a/libs/libcurl/src/inet_pton.c
+++ b/libs/libcurl/src/inet_pton.c
@@ -112,7 +112,8 @@ inet_pton4(const char *src, unsigned char *dst)
pch = strchr(digits, ch);
if(pch) {
- unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
+ unsigned int val = (unsigned int)(*tp * 10) +
+ (unsigned int)(pch - digits);
if(saw_digit && *tp == 0)
return (0);
diff --git a/libs/libcurl/src/inet_pton.h b/libs/libcurl/src/inet_pton.h
index 2d23c77a30..c3dc5d0415 100644
--- a/libs/libcurl/src/inet_pton.h
+++ b/libs/libcurl/src/inet_pton.h
@@ -31,9 +31,6 @@ int Curl_inet_pton(int, const char *, void *);
#ifdef HAVE_INET_PTON
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
-#elif defined(HAVE_WS2TCPIP_H)
-/* inet_pton() exists in Vista or later */
-#include <ws2tcpip.h>
#endif
#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
#endif
diff --git a/libs/libcurl/src/krb5.c b/libs/libcurl/src/krb5.c
index d69be2c17a..6c245c575d 100644
--- a/libs/libcurl/src/krb5.c
+++ b/libs/libcurl/src/krb5.c
@@ -75,8 +75,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
unsigned char data_sec = conn->data_prot;
#endif
- if(!cmd)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ DEBUGASSERT(cmd);
write_len = strlen(cmd);
if(!write_len || write_len > (sizeof(s) -3))
@@ -236,9 +235,12 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
if(Curl_GetFTPResponse(data, &nread, NULL))
return -1;
-
- if(data->state.buffer[0] != '3')
- return -1;
+ else {
+ struct pingpong *pp = &conn->proto.ftpc.pp;
+ char *line = Curl_dyn_ptr(&pp->recvbuf);
+ if(line[0] != '3')
+ return -1;
+ }
}
stringp = aprintf("%s@%s", service, host);
@@ -322,15 +324,19 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
ret = -1;
break;
}
-
- if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
- infof(data, "Server didn't accept auth data");
- ret = AUTH_ERROR;
- break;
+ else {
+ struct pingpong *pp = &conn->proto.ftpc.pp;
+ size_t len = Curl_dyn_len(&pp->recvbuf);
+ p = Curl_dyn_ptr(&pp->recvbuf);
+ if((len < 4) || (p[0] != '2' && p[0] != '3')) {
+ infof(data, "Server didn't accept auth data");
+ ret = AUTH_ERROR;
+ break;
+ }
}
_gssresp.value = NULL; /* make sure it is initialized */
- p = data->state.buffer + 4;
+ p += 4; /* over '789 ' */
p = strstr(p, "ADAT=");
if(p) {
result = Curl_base64_decode(p + 5,
@@ -417,7 +423,6 @@ static char level_to_char(int level)
case PROT_PRIVATE:
return 'P';
case PROT_CMD:
- /* Fall through */
default:
/* Those 2 cases should not be reached! */
break;
@@ -430,6 +435,9 @@ static char level_to_char(int level)
/* Send an FTP command defined by |message| and the optional arguments. The
function returns the ftp_code. If an error occurs, -1 is returned. */
static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
+ CURL_PRINTF(2, 3);
+
+static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
{
int ftp_code;
ssize_t nread = 0;
@@ -750,6 +758,8 @@ static int sec_set_protection_level(struct Curl_easy *data)
if(level) {
char *pbsz;
unsigned int buffer_size = 1 << 20; /* 1048576 */
+ struct pingpong *pp = &conn->proto.ftpc.pp;
+ char *line;
code = ftp_send_command(data, "PBSZ %u", buffer_size);
if(code < 0)
@@ -761,10 +771,11 @@ static int sec_set_protection_level(struct Curl_easy *data)
}
conn->buffer_size = buffer_size;
- pbsz = strstr(data->state.buffer, "PBSZ=");
+ line = Curl_dyn_ptr(&pp->recvbuf);
+ pbsz = strstr(line, "PBSZ=");
if(pbsz) {
/* stick to default value if the check fails */
- if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5]))
+ if(ISDIGIT(pbsz[5]))
buffer_size = atoi(&pbsz[5]);
if(buffer_size < conn->buffer_size)
conn->buffer_size = buffer_size;
diff --git a/libs/libcurl/src/ldap.c b/libs/libcurl/src/ldap.c
index 16e851dcec..9d8bee81a0 100644
--- a/libs/libcurl/src/ldap.c
+++ b/libs/libcurl/src/ldap.c
@@ -137,7 +137,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp);
_ldap_trace x; \
} while(0)
- static void _ldap_trace(const char *fmt, ...);
+ static void _ldap_trace(const char *fmt, ...) CURL_PRINTF(1, 2);
#else
#define LDAP_TRACE(x) Curl_nop_stmt
#endif
@@ -177,7 +177,7 @@ const struct Curl_handler Curl_handler_ldap = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAP, /* defport */
@@ -205,7 +205,7 @@ const struct Curl_handler Curl_handler_ldaps = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAPS, /* defport */
diff --git a/libs/libcurl/src/libcurl.plist b/libs/libcurl/src/libcurl.plist
index 7c4364f8d8..f9df74ffe0 100644
--- a/libs/libcurl/src/libcurl.plist
+++ b/libs/libcurl/src/libcurl.plist
@@ -15,7 +15,7 @@
<string>se.curl.libcurl</string>
<key>CFBundleVersion</key>
- <string>8.5.0</string>
+ <string>8.6.0</string>
<key>CFBundleName</key>
<string>libcurl</string>
@@ -27,9 +27,9 @@
<string>????</string>
<key>CFBundleShortVersionString</key>
- <string>libcurl 8.5.0</string>
+ <string>libcurl 8.6.0</string>
<key>CFBundleGetInfoString</key>
- <string>libcurl.plist 8.5.0</string>
+ <string>libcurl.plist 8.6.0</string>
</dict>
</plist>
diff --git a/libs/libcurl/src/md4.c b/libs/libcurl/src/md4.c
index 7aae719266..f48ea9987c 100644
--- a/libs/libcurl/src/md4.c
+++ b/libs/libcurl/src/md4.c
@@ -194,11 +194,9 @@ static int MD4_Init(MD4_CTX *ctx)
static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
{
if(!ctx->data) {
- ctx->data = malloc(size);
- if(ctx->data) {
- memcpy(ctx->data, data, size);
+ ctx->data = Curl_memdup(data, size);
+ if(ctx->data)
ctx->size = size;
- }
}
}
diff --git a/libs/libcurl/src/memdebug.c b/libs/libcurl/src/memdebug.c
index 869d9ca54d..5d1bb4ad55 100644
--- a/libs/libcurl/src/memdebug.c
+++ b/libs/libcurl/src/memdebug.c
@@ -304,12 +304,6 @@ void curl_dbg_free(void *ptr, int line, const char *source)
curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
int line, const char *source)
{
- const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
- "FD %s:%d socket() = %d\n" :
- (sizeof(curl_socket_t) == sizeof(long)) ?
- "FD %s:%d socket() = %ld\n" :
- "FD %s:%d socket() = %zd\n";
-
curl_socket_t sockfd;
if(countcheck("socket", line, source))
@@ -318,7 +312,8 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
sockfd = socket(domain, type, protocol);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_dbg_log(fmt, source, line, sockfd);
+ curl_dbg_log("FD %s:%d socket() = %" CURL_FORMAT_SOCKET_T "\n",
+ source, line, sockfd);
return sockfd;
}
@@ -357,16 +352,12 @@ int curl_dbg_socketpair(int domain, int type, int protocol,
curl_socket_t socket_vector[2],
int line, const char *source)
{
- const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
- "FD %s:%d socketpair() = %d %d\n" :
- (sizeof(curl_socket_t) == sizeof(long)) ?
- "FD %s:%d socketpair() = %ld %ld\n" :
- "FD %s:%d socketpair() = %zd %zd\n";
-
int res = socketpair(domain, type, protocol, socket_vector);
if(source && (0 == res))
- curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
+ curl_dbg_log("FD %s:%d socketpair() = "
+ "%" CURL_FORMAT_SOCKET_T " %" CURL_FORMAT_SOCKET_T "\n",
+ source, line, socket_vector[0], socket_vector[1]);
return res;
}
@@ -375,19 +366,14 @@ int curl_dbg_socketpair(int domain, int type, int protocol,
curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
int line, const char *source)
{
- const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
- "FD %s:%d accept() = %d\n" :
- (sizeof(curl_socket_t) == sizeof(long)) ?
- "FD %s:%d accept() = %ld\n" :
- "FD %s:%d accept() = %zd\n";
-
struct sockaddr *addr = (struct sockaddr *)saddr;
curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
curl_socket_t sockfd = accept(s, addr, addrlen);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_dbg_log(fmt, source, line, sockfd);
+ curl_dbg_log("FD %s:%d accept() = %" CURL_FORMAT_SOCKET_T "\n",
+ source, line, sockfd);
return sockfd;
}
@@ -395,14 +381,9 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
/* separate function to allow libcurl to mark a "faked" close */
void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
{
- const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
- "FD %s:%d sclose(%d)\n":
- (sizeof(curl_socket_t) == sizeof(long)) ?
- "FD %s:%d sclose(%ld)\n":
- "FD %s:%d sclose(%zd)\n";
-
if(source)
- curl_dbg_log(fmt, source, line, sockfd);
+ curl_dbg_log("FD %s:%d sclose(%" CURL_FORMAT_SOCKET_T ")\n",
+ source, line, sockfd);
}
/* this is our own defined way to close sockets on *ALL* platforms */
diff --git a/libs/libcurl/src/memdebug.h b/libs/libcurl/src/memdebug.h
index a3a701011e..c3c81bad2c 100644
--- a/libs/libcurl/src/memdebug.h
+++ b/libs/libcurl/src/memdebug.h
@@ -72,7 +72,7 @@ CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
CURL_EXTERN void curl_dbg_memdebug(const char *logname);
CURL_EXTERN void curl_dbg_memlimit(long limit);
-CURL_EXTERN void curl_dbg_log(const char *format, ...);
+CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2);
/* file descriptor manipulators */
CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
diff --git a/libs/libcurl/src/mime.c b/libs/libcurl/src/mime.c
index 007a187bf5..167c8bab2d 100644
--- a/libs/libcurl/src/mime.c
+++ b/libs/libcurl/src/mime.c
@@ -30,6 +30,7 @@
#include "warnless.h"
#include "urldata.h"
#include "sendf.h"
+#include "strdup.h"
#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
!defined(CURL_DISABLE_SMTP) || \
@@ -817,7 +818,7 @@ static size_t read_part_content(curl_mimepart *part,
case MIMEKIND_FILE:
if(part->fp && feof(part->fp))
break; /* At EOF. */
- /* FALLTHROUGH */
+ FALLTHROUGH();
default:
if(part->readfunc) {
if(!(part->flags & MIME_FAST_READ)) {
@@ -936,7 +937,7 @@ static size_t readback_part(curl_mimepart *part,
mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
break;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case MIMESTATE_CURLHEADERS:
if(!hdr)
mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
@@ -970,7 +971,7 @@ static size_t readback_part(curl_mimepart *part,
fclose(part->fp);
part->fp = NULL;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_READFUNC_ABORT:
case CURL_READFUNC_PAUSE:
case READ_ERROR:
@@ -1235,6 +1236,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data,
}
break;
default: /* Invalid kind: should not occur. */
+ DEBUGF(infof(data, "invalid MIMEKIND* attempt"));
res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */
break;
}
@@ -1370,27 +1372,22 @@ CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
/* Set mime part content from memory data. */
CURLcode curl_mime_data(curl_mimepart *part,
- const char *data, size_t datasize)
+ const char *ptr, size_t datasize)
{
if(!part)
return CURLE_BAD_FUNCTION_ARGUMENT;
cleanup_part_content(part);
- if(data) {
+ if(ptr) {
if(datasize == CURL_ZERO_TERMINATED)
- datasize = strlen(data);
+ datasize = strlen(ptr);
- part->data = malloc(datasize + 1);
+ part->data = Curl_memdup0(ptr, datasize);
if(!part->data)
return CURLE_OUT_OF_MEMORY;
part->datasize = datasize;
-
- if(datasize)
- memcpy(part->data, data, datasize);
- part->data[datasize] = '\0'; /* Set a null terminator as sentinel. */
-
part->readfunc = mime_mem_read;
part->seekfunc = mime_mem_seek;
part->freefunc = mime_mem_free;
diff --git a/libs/libcurl/src/mime.h b/libs/libcurl/src/mime.h
index 359fceea04..627689178b 100644
--- a/libs/libcurl/src/mime.h
+++ b/libs/libcurl/src/mime.h
@@ -130,7 +130,8 @@ struct curl_mimepart {
size_t lastreadstatus; /* Last read callback returned status. */
};
-CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+ CURL_PRINTF(2, 3);
#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
!defined(CURL_DISABLE_SMTP) || \
diff --git a/libs/libcurl/src/mprintf.c b/libs/libcurl/src/mprintf.c
index cb91b6429a..72cf27edd3 100644
--- a/libs/libcurl/src/mprintf.c
+++ b/libs/libcurl/src/mprintf.c
@@ -20,25 +20,11 @@
*
* SPDX-License-Identifier: curl
*
- *
- * Purpose:
- * A merge of Bjorn Reese's format() function and Daniel's dsprintf()
- * 1.0. A full blooded printf() clone with full support for <num>$
- * everywhere (parameters, widths and precisions) including variabled
- * sized parameters (like doubles, long longs, long doubles and even
- * void * in 64-bit architectures).
- *
- * Current restrictions:
- * - Max 128 parameters
- * - No 'long double' support.
- *
- * If you ever want truly portable and good *printf() clones, the project that
- * took on from here is named 'Trio' and you find more details on the trio web
- * page at https://daniel.haxx.se/projects/trio/
*/
#include "curl_setup.h"
#include "dynbuf.h"
+#include "curl_printf.h"
#include <curl/mprintf.h>
#include "curl_memory.h"
@@ -86,7 +72,8 @@
#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
fit negative DBL_MAX (317 letters) */
-#define MAX_PARAMETERS 128 /* lame static limit */
+#define MAX_PARAMETERS 128 /* number of input arguments */
+#define MAX_SEGMENTS 128 /* number of output segments */
#ifdef __AMIGA__
# undef FORMAT_INT
@@ -98,31 +85,33 @@ static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
/* Upper-case digits. */
static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-#define OUTCHAR(x) \
- do { \
- if(stream((unsigned char)(x), (FILE *)data) != -1) \
- done++; \
- else \
- return done; /* return immediately on failure */ \
+#define OUTCHAR(x) \
+ do { \
+ if(!stream(x, userp)) \
+ done++; \
+ else \
+ return done; /* return on failure */ \
} while(0)
/* Data type to read from the arglist */
typedef enum {
- FORMAT_UNKNOWN = 0,
FORMAT_STRING,
FORMAT_PTR,
- FORMAT_INT,
FORMAT_INTPTR,
+ FORMAT_INT,
FORMAT_LONG,
FORMAT_LONGLONG,
+ FORMAT_INTU,
+ FORMAT_LONGU,
+ FORMAT_LONGLONGU,
FORMAT_DOUBLE,
FORMAT_LONGDOUBLE,
- FORMAT_WIDTH /* For internal use */
+ FORMAT_WIDTH,
+ FORMAT_PRECISION
} FormatType;
/* conversion and display flags */
enum {
- FLAGS_NEW = 0,
FLAGS_SPACE = 1<<0,
FLAGS_SHOWSIGN = 1<<1,
FLAGS_LEFT = 1<<2,
@@ -142,23 +131,40 @@ enum {
FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
FLAGS_CHAR = 1<<17, /* %c story */
FLAGS_FLOATE = 1<<18, /* %e or %E */
- FLAGS_FLOATG = 1<<19 /* %g or %G */
+ FLAGS_FLOATG = 1<<19, /* %g or %G */
+ FLAGS_SUBSTR = 1<<20 /* no input, only substring */
};
-struct va_stack {
- FormatType type;
- int flags;
- long width; /* width OR width parameter number */
- long precision; /* precision OR precision parameter number */
+enum {
+ DOLLAR_UNKNOWN,
+ DOLLAR_NOPE,
+ DOLLAR_USE
+};
+
+/*
+ * Describes an input va_arg type and hold its value.
+ */
+struct va_input {
+ FormatType type; /* FormatType */
union {
char *str;
void *ptr;
- union {
- mp_intmax_t as_signed;
- mp_uintmax_t as_unsigned;
- } num;
+ mp_intmax_t nums; /* signed */
+ mp_uintmax_t numu; /* unsigned */
double dnum;
- } data;
+ } val;
+};
+
+/*
+ * Describes an output segment.
+ */
+struct outsegment {
+ int width; /* width OR width parameter number */
+ int precision; /* precision OR precision parameter number */
+ unsigned int flags;
+ unsigned int input; /* input argument array index */
+ char *start; /* format string start to output */
+ size_t outlen; /* number of bytes from the format string to output */
};
struct nsprintf {
@@ -169,118 +175,123 @@ struct nsprintf {
struct asprintf {
struct dynbuf *b;
- bool fail; /* if an alloc has failed and thus the output is not the complete
- data */
+ char merr;
};
-static long dprintf_DollarString(char *input, char **end)
-{
- int number = 0;
- while(ISDIGIT(*input)) {
- if(number < MAX_PARAMETERS) {
- number *= 10;
- number += *input - '0';
- }
- input++;
- }
- if(number <= MAX_PARAMETERS && ('$' == *input)) {
- *end = ++input;
- return number;
- }
- return 0;
-}
+/* the provided input number is 1-based but this returns the number 0-based.
-static bool dprintf_IsQualifierNoDollar(const char *fmt)
+ returns -1 if no valid number was provided.
+*/
+static int dollarstring(char *input, char **end)
{
-#if defined(MP_HAVE_INT_EXTENSIONS)
- if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
- return TRUE;
- }
-#endif
-
- switch(*fmt) {
- case '-': case '+': case ' ': case '#': case '.':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case 'h': case 'l': case 'L': case 'z': case 'q':
- case '*': case 'O':
-#if defined(MP_HAVE_INT_EXTENSIONS)
- case 'I':
-#endif
- return TRUE;
+ if(ISDIGIT(*input)) {
+ int number = 0;
+ do {
+ if(number < MAX_PARAMETERS) {
+ number *= 10;
+ number += *input - '0';
+ }
+ input++;
+ } while(ISDIGIT(*input));
- default:
- return FALSE;
+ if(number && (number <= MAX_PARAMETERS) && ('$' == *input)) {
+ *end = ++input;
+ return number - 1;
+ }
}
+ return -1;
}
-/******************************************************************
+/*
+ * Parse the format string.
*
- * Pass 1:
- * Create an index with the type of each parameter entry and its
- * value (may vary in size)
+ * Create two arrays. One describes the inputs, one describes the outputs.
*
* Returns zero on success.
- *
- ******************************************************************/
+ */
-static int dprintf_Pass1(const char *format, struct va_stack *vto,
- char **endpos, va_list arglist)
+#define PFMT_OK 0
+#define PFMT_DOLLAR 1 /* bad dollar for main param */
+#define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */
+#define PFMT_DOLLARPREC 3 /* bad dollar use for precision */
+#define PFMT_MANYARGS 4 /* too many input arguments used */
+#define PFMT_PREC 5 /* precision overflow */
+#define PFMT_PRECMIX 6 /* bad mix of precision specifiers */
+#define PFMT_WIDTH 7 /* width overflow */
+#define PFMT_INPUTGAP 8 /* gap in arguments */
+#define PFMT_WIDTHARG 9 /* attempted to use same arg twice, for width */
+#define PFMT_PRECARG 10 /* attempted to use same arg twice, for prec */
+#define PFMT_MANYSEGS 11 /* maxed out output segments */
+
+static int parsefmt(const char *format,
+ struct outsegment *out,
+ struct va_input *in,
+ int *opieces,
+ int *ipieces, va_list arglist)
{
char *fmt = (char *)format;
int param_num = 0;
- long this_param;
- long width;
- long precision;
- int flags;
- long max_param = 0;
- long i;
+ int param;
+ int width;
+ int precision;
+ unsigned int flags;
+ FormatType type;
+ int max_param = -1;
+ int i;
+ int ocount = 0;
+ unsigned char usedinput[MAX_PARAMETERS/8];
+ size_t outlen = 0;
+ struct outsegment *optr;
+ int use_dollar = DOLLAR_UNKNOWN;
+ char *start = fmt;
+
+ /* clear, set a bit for each used input */
+ memset(usedinput, 0, sizeof(usedinput));
while(*fmt) {
- if(*fmt++ == '%') {
+ if(*fmt == '%') {
+ struct va_input *iptr;
+ bool loopit = TRUE;
+ fmt++;
+ outlen = fmt - start - 1;
if(*fmt == '%') {
+ /* this means a %% that should be output only as %. Create an output
+ segment. */
+ if(outlen) {
+ optr = &out[ocount++];
+ if(ocount > MAX_SEGMENTS)
+ return PFMT_MANYSEGS;
+ optr->input = 0;
+ optr->flags = FLAGS_SUBSTR;
+ optr->start = start;
+ optr->outlen = outlen;
+ }
+ start = fmt;
fmt++;
continue; /* while */
}
- flags = FLAGS_NEW;
-
- /* Handle the positional case (N$) */
-
- param_num++;
-
- this_param = dprintf_DollarString(fmt, &fmt);
- if(0 == this_param)
- /* we got no positional, get the next counter */
- this_param = param_num;
-
- if(this_param > max_param)
- max_param = this_param;
+ flags = width = precision = 0;
- /*
- * The parameter with number 'i' should be used. Next, we need
- * to get SIZE and TYPE of the parameter. Add the information
- * to our array.
- */
+ if(use_dollar != DOLLAR_NOPE) {
+ param = dollarstring(fmt, &fmt);
+ if(param < 0) {
+ if(use_dollar == DOLLAR_USE)
+ /* illegal combo */
+ return PFMT_DOLLAR;
- width = 0;
- precision = 0;
-
- /* Handle the flags */
-
- while(dprintf_IsQualifierNoDollar(fmt)) {
-#if defined(MP_HAVE_INT_EXTENSIONS)
- if(!strncmp(fmt, "I32", 3)) {
- flags |= FLAGS_LONG;
- fmt += 3;
- }
- else if(!strncmp(fmt, "I64", 3)) {
- flags |= FLAGS_LONGLONG;
- fmt += 3;
+ /* we got no positional, just get the next arg */
+ param = -1;
+ use_dollar = DOLLAR_NOPE;
}
else
-#endif
+ use_dollar = DOLLAR_USE;
+ }
+ else
+ param = -1;
+ /* Handle the flags */
+ while(loopit) {
switch(*fmt++) {
case ' ':
flags |= FLAGS_SPACE;
@@ -298,40 +309,63 @@ static int dprintf_Pass1(const char *format, struct va_stack *vto,
case '.':
if('*' == *fmt) {
/* The precision is picked from a specified parameter */
-
flags |= FLAGS_PRECPARAM;
fmt++;
- param_num++;
- i = dprintf_DollarString(fmt, &fmt);
- if(i)
- precision = i;
+ if(use_dollar == DOLLAR_USE) {
+ precision = dollarstring(fmt, &fmt);
+ if(precision < 0)
+ /* illegal combo */
+ return PFMT_DOLLARPREC;
+ }
else
- precision = param_num;
-
- if(precision > max_param)
- max_param = precision;
+ /* get it from the next argument */
+ precision = -1;
}
else {
+ bool is_neg = FALSE;
flags |= FLAGS_PREC;
- precision = strtol(fmt, &fmt, 10);
+ precision = 0;
+ if('-' == *fmt) {
+ is_neg = TRUE;
+ fmt++;
+ }
+ while(ISDIGIT(*fmt)) {
+ if(precision > INT_MAX/10)
+ return PFMT_PREC;
+ precision *= 10;
+ precision += *fmt - '0';
+ fmt++;
+ }
+ if(is_neg)
+ precision = -precision;
}
if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
(FLAGS_PREC | FLAGS_PRECPARAM))
/* it is not permitted to use both kinds of precision for the same
argument */
- return 1;
+ return PFMT_PRECMIX;
break;
case 'h':
flags |= FLAGS_SHORT;
break;
#if defined(MP_HAVE_INT_EXTENSIONS)
case 'I':
+ if((fmt[0] == '3') && (fmt[1] == '2')) {
+ flags |= FLAGS_LONG;
+ fmt += 2;
+ }
+ else if((fmt[0] == '6') && (fmt[1] == '4')) {
+ flags |= FLAGS_LONGLONG;
+ fmt += 2;
+ }
+ else {
#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
- flags |= FLAGS_LONGLONG;
+ flags |= FLAGS_LONGLONG;
#else
- flags |= FLAGS_LONG;
+ flags |= FLAGS_LONG;
#endif
+ }
break;
#endif
case 'l':
@@ -365,401 +399,421 @@ static int dprintf_Pass1(const char *format, struct va_stack *vto,
case '0':
if(!(flags & FLAGS_LEFT))
flags |= FLAGS_PAD_NIL;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
flags |= FLAGS_WIDTH;
- width = strtol(fmt-1, &fmt, 10);
+ width = 0;
+ fmt--;
+ do {
+ if(width > INT_MAX/10)
+ return PFMT_WIDTH;
+ width *= 10;
+ width += *fmt - '0';
+ fmt++;
+ } while(ISDIGIT(*fmt));
break;
- case '*': /* Special case */
+ case '*': /* read width from argument list */
flags |= FLAGS_WIDTHPARAM;
- param_num++;
-
- i = dprintf_DollarString(fmt, &fmt);
- if(i)
- width = i;
+ if(use_dollar == DOLLAR_USE) {
+ width = dollarstring(fmt, &fmt);
+ if(width < 0)
+ /* illegal combo */
+ return PFMT_DOLLARWIDTH;
+ }
else
- width = param_num;
- if(width > max_param)
- max_param = width;
+ /* pick from the next argument */
+ width = -1;
break;
- case '\0':
- fmt--;
default:
+ loopit = FALSE;
+ fmt--;
break;
- }
- } /* switch */
-
- /* Handle the specifier */
-
- i = this_param - 1;
-
- if((i < 0) || (i >= MAX_PARAMETERS))
- /* out of allowed range */
- return 1;
+ } /* switch */
+ } /* while */
switch(*fmt) {
case 'S':
flags |= FLAGS_ALT;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 's':
- vto[i].type = FORMAT_STRING;
+ type = FORMAT_STRING;
break;
case 'n':
- vto[i].type = FORMAT_INTPTR;
+ type = FORMAT_INTPTR;
break;
case 'p':
- vto[i].type = FORMAT_PTR;
+ type = FORMAT_PTR;
break;
- case 'd': case 'i':
- vto[i].type = FORMAT_INT;
+ case 'd':
+ case 'i':
+ if(flags & FLAGS_LONGLONG)
+ type = FORMAT_LONGLONG;
+ else if(flags & FLAGS_LONG)
+ type = FORMAT_LONG;
+ else
+ type = FORMAT_INT;
break;
case 'u':
- vto[i].type = FORMAT_INT;
+ if(flags & FLAGS_LONGLONG)
+ type = FORMAT_LONGLONGU;
+ else if(flags & FLAGS_LONG)
+ type = FORMAT_LONGU;
+ else
+ type = FORMAT_INTU;
flags |= FLAGS_UNSIGNED;
break;
case 'o':
- vto[i].type = FORMAT_INT;
+ type = FORMAT_INT;
flags |= FLAGS_OCTAL;
break;
case 'x':
- vto[i].type = FORMAT_INT;
+ type = FORMAT_INTU;
flags |= FLAGS_HEX|FLAGS_UNSIGNED;
break;
case 'X':
- vto[i].type = FORMAT_INT;
+ type = FORMAT_INTU;
flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
break;
case 'c':
- vto[i].type = FORMAT_INT;
+ type = FORMAT_INT;
flags |= FLAGS_CHAR;
break;
case 'f':
- vto[i].type = FORMAT_DOUBLE;
+ type = FORMAT_DOUBLE;
break;
case 'e':
- vto[i].type = FORMAT_DOUBLE;
+ type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE;
break;
case 'E':
- vto[i].type = FORMAT_DOUBLE;
+ type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATE|FLAGS_UPPER;
break;
case 'g':
- vto[i].type = FORMAT_DOUBLE;
+ type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG;
break;
case 'G':
- vto[i].type = FORMAT_DOUBLE;
+ type = FORMAT_DOUBLE;
flags |= FLAGS_FLOATG|FLAGS_UPPER;
break;
default:
- vto[i].type = FORMAT_UNKNOWN;
- break;
+ /* invalid instruction, disregard and continue */
+ continue;
} /* switch */
- vto[i].flags = flags;
- vto[i].width = width;
- vto[i].precision = precision;
-
if(flags & FLAGS_WIDTHPARAM) {
- /* we have the width specified from a parameter, so we make that
- parameter's info setup properly */
- long k = width - 1;
- if((k < 0) || (k >= MAX_PARAMETERS))
- /* out of allowed range */
- return 1;
- vto[i].width = k;
- vto[k].type = FORMAT_WIDTH;
- vto[k].flags = FLAGS_NEW;
- /* can't use width or precision of width! */
- vto[k].width = 0;
- vto[k].precision = 0;
+ if(width < 0)
+ width = param_num++;
+ else {
+ /* if this identifies a parameter already used, this
+ is illegal */
+ if(usedinput[width/8] & (1 << (width&7)))
+ return PFMT_WIDTHARG;
+ }
+ if(width >= MAX_PARAMETERS)
+ return PFMT_MANYARGS;
+ if(width >= max_param)
+ max_param = width;
+
+ in[width].type = FORMAT_WIDTH;
+ /* mark as used */
+ usedinput[width/8] |= (unsigned char)(1 << (width&7));
}
+
if(flags & FLAGS_PRECPARAM) {
- /* we have the precision specified from a parameter, so we make that
- parameter's info setup properly */
- long k = precision - 1;
- if((k < 0) || (k >= MAX_PARAMETERS))
- /* out of allowed range */
- return 1;
- vto[i].precision = k;
- vto[k].type = FORMAT_WIDTH;
- vto[k].flags = FLAGS_NEW;
- /* can't use width or precision of width! */
- vto[k].width = 0;
- vto[k].precision = 0;
+ if(precision < 0)
+ precision = param_num++;
+ else {
+ /* if this identifies a parameter already used, this
+ is illegal */
+ if(usedinput[precision/8] & (1 << (precision&7)))
+ return PFMT_PRECARG;
+ }
+ if(precision >= MAX_PARAMETERS)
+ return PFMT_MANYARGS;
+ if(precision >= max_param)
+ max_param = precision;
+
+ in[precision].type = FORMAT_PRECISION;
+ usedinput[precision/8] |= (unsigned char)(1 << (precision&7));
}
- *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
+
+ /* Handle the specifier */
+ if(param < 0)
+ param = param_num++;
+ if(param >= MAX_PARAMETERS)
+ return PFMT_MANYARGS;
+ if(param >= max_param)
+ max_param = param;
+
+ iptr = &in[param];
+ iptr->type = type;
+
+ /* mark this input as used */
+ usedinput[param/8] |= (unsigned char)(1 << (param&7));
+
+ fmt++;
+ optr = &out[ocount++];
+ if(ocount > MAX_SEGMENTS)
+ return PFMT_MANYSEGS;
+ optr->input = param;
+ optr->flags = flags;
+ optr->width = width;
+ optr->precision = precision;
+ optr->start = start;
+ optr->outlen = outlen;
+ start = fmt;
}
+ else
+ fmt++;
}
- /* Read the arg list parameters into our data list */
- for(i = 0; i<max_param; i++) {
- /* Width/precision arguments must be read before the main argument
- they are attached to */
- if(vto[i].flags & FLAGS_WIDTHPARAM) {
- vto[vto[i].width].data.num.as_signed =
- (mp_intmax_t)va_arg(arglist, int);
- }
- if(vto[i].flags & FLAGS_PRECPARAM) {
- vto[vto[i].precision].data.num.as_signed =
- (mp_intmax_t)va_arg(arglist, int);
- }
+ /* is there a trailing piece */
+ outlen = fmt - start;
+ if(outlen) {
+ optr = &out[ocount++];
+ if(ocount > MAX_SEGMENTS)
+ return PFMT_MANYSEGS;
+ optr->input = 0;
+ optr->flags = FLAGS_SUBSTR;
+ optr->start = start;
+ optr->outlen = outlen;
+ }
- switch(vto[i].type) {
+ /* Read the arg list parameters into our data list */
+ for(i = 0; i < max_param + 1; i++) {
+ struct va_input *iptr = &in[i];
+ if(!(usedinput[i/8] & (1 << (i&7))))
+ /* bad input */
+ return PFMT_INPUTGAP;
+
+ /* based on the type, read the correct argument */
+ switch(iptr->type) {
case FORMAT_STRING:
- vto[i].data.str = va_arg(arglist, char *);
+ iptr->val.str = va_arg(arglist, char *);
break;
case FORMAT_INTPTR:
- case FORMAT_UNKNOWN:
case FORMAT_PTR:
- vto[i].data.ptr = va_arg(arglist, void *);
+ iptr->val.ptr = va_arg(arglist, void *);
break;
- case FORMAT_INT:
-#ifdef HAVE_LONG_LONG_TYPE
- if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
- vto[i].data.num.as_unsigned =
- (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
- else if(vto[i].flags & FLAGS_LONGLONG)
- vto[i].data.num.as_signed =
- (mp_intmax_t)va_arg(arglist, mp_intmax_t);
- else
-#endif
- {
- if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
- vto[i].data.num.as_unsigned =
- (mp_uintmax_t)va_arg(arglist, unsigned long);
- else if(vto[i].flags & FLAGS_LONG)
- vto[i].data.num.as_signed =
- (mp_intmax_t)va_arg(arglist, long);
- else if(vto[i].flags & FLAGS_UNSIGNED)
- vto[i].data.num.as_unsigned =
- (mp_uintmax_t)va_arg(arglist, unsigned int);
- else
- vto[i].data.num.as_signed =
- (mp_intmax_t)va_arg(arglist, int);
- }
+ case FORMAT_LONGLONGU:
+ iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
break;
- case FORMAT_DOUBLE:
- vto[i].data.dnum = va_arg(arglist, double);
+ case FORMAT_LONGLONG:
+ iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t);
+ break;
+
+ case FORMAT_LONGU:
+ iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long);
+ break;
+
+ case FORMAT_LONG:
+ iptr->val.nums = (mp_intmax_t)va_arg(arglist, long);
+ break;
+
+ case FORMAT_INTU:
+ iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int);
break;
+ case FORMAT_INT:
case FORMAT_WIDTH:
- /* Argument has been read. Silently convert it into an integer
- * for later use
- */
- vto[i].type = FORMAT_INT;
+ case FORMAT_PRECISION:
+ iptr->val.nums = (mp_intmax_t)va_arg(arglist, int);
+ break;
+
+ case FORMAT_DOUBLE:
+ iptr->val.dnum = va_arg(arglist, double);
break;
default:
+ DEBUGASSERT(NULL); /* unexpected */
break;
}
}
+ *ipieces = max_param + 1;
+ *opieces = ocount;
- return 0;
-
+ return PFMT_OK;
}
-static int dprintf_formatf(
- void *data, /* untouched by format(), just sent to the stream() function in
- the second argument */
+/*
+ * formatf() - the general printf function.
+ *
+ * It calls parsefmt() to parse the format string. It populates two arrays;
+ * one that describes the input arguments and one that describes a number of
+ * output segments.
+ *
+ * On success, the input array describes the type of all arguments and their
+ * values.
+ *
+ * The function then iterates over the output sengments and outputs them one
+ * by one until done. Using the appropriate input arguments (if any).
+ *
+ * All output is sent to the 'stream()' callback, one byte at a time.
+ */
+
+static int formatf(
+ void *userp, /* untouched by format(), just sent to the stream() function in
+ the second argument */
/* function pointer called for each output character */
- int (*stream)(int, FILE *),
+ int (*stream)(unsigned char, void *),
const char *format, /* %-formatted string */
va_list ap_save) /* list of parameters */
{
- /* Base-36 digits for numbers. */
- const char *digits = lower_digits;
-
- /* Pointer into the format string. */
- char *f;
-
- /* Number of characters written. */
- int done = 0;
-
- long param; /* current parameter to read */
- long param_num = 0; /* parameter counter */
-
- struct va_stack vto[MAX_PARAMETERS];
- char *endpos[MAX_PARAMETERS];
- char **end;
+ static const char nilstr[] = "(nil)";
+ const char *digits = lower_digits; /* Base-36 digits for numbers. */
+ int done = 0; /* number of characters written */
+ int i;
+ int ocount = 0; /* number of output segments */
+ int icount = 0; /* number of input arguments */
+
+ struct outsegment output[MAX_SEGMENTS];
+ struct va_input input[MAX_PARAMETERS];
char work[BUFFSIZE];
- struct va_stack *p;
/* 'workend' points to the final buffer byte position, but with an extra
byte as margin to avoid the (false?) warning Coverity gives us
otherwise */
char *workend = &work[sizeof(work) - 2];
- /* Do the actual %-code parsing */
- if(dprintf_Pass1(format, vto, endpos, ap_save))
+ /* Parse the format string */
+ if(parsefmt(format, output, input, &ocount, &icount, ap_save))
return 0;
- end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
- created for us */
-
- f = (char *)format;
- while(*f != '\0') {
- /* Format spec modifiers. */
- int is_alt;
-
- /* Width of a field. */
- long width;
-
- /* Precision of a field. */
- long prec;
-
- /* Decimal integer is negative. */
- int is_neg;
-
- /* Base of a number to be written. */
- unsigned long base;
-
- /* Integral values to be written. */
- mp_uintmax_t num;
-
- /* Used to convert negative in positive. */
- mp_intmax_t signed_num;
-
+ for(i = 0; i < ocount; i++) {
+ struct outsegment *optr = &output[i];
+ struct va_input *iptr;
+ bool is_alt; /* Format spec modifiers. */
+ int width; /* Width of a field. */
+ int prec; /* Precision of a field. */
+ bool is_neg; /* Decimal integer is negative. */
+ unsigned long base; /* Base of a number to be written. */
+ mp_uintmax_t num; /* Integral values to be written. */
+ mp_intmax_t signed_num; /* Used to convert negative in positive. */
char *w;
-
- if(*f != '%') {
- /* This isn't a format spec, so write everything out until the next one
- OR end of string is reached. */
- do {
- OUTCHAR(*f);
- } while(*++f && ('%' != *f));
- continue;
+ size_t outlen = optr->outlen;
+ int flags = optr->flags;
+
+ if(outlen) {
+ char *str = optr->start;
+ for(; outlen && *str; outlen--)
+ OUTCHAR(*str++);
+ if(optr->flags & FLAGS_SUBSTR)
+ /* this is just a substring */
+ continue;
}
- ++f;
-
- /* Check for "%%". Note that although the ANSI standard lists
- '%' as a conversion specifier, it says "The complete format
- specification shall be `%%'," so we can avoid all the width
- and precision processing. */
- if(*f == '%') {
- ++f;
- OUTCHAR('%');
- continue;
- }
-
- /* If this is a positional parameter, the position must follow immediately
- after the %, thus create a %<num>$ sequence */
- param = dprintf_DollarString(f, &f);
-
- if(!param)
- param = param_num;
- else
- --param;
-
- param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
- third %s will pick the 3rd argument */
-
- p = &vto[param];
-
/* pick up the specified width */
- if(p->flags & FLAGS_WIDTHPARAM) {
- width = (long)vto[p->width].data.num.as_signed;
- param_num++; /* since the width is extracted from a parameter, we
- must skip that to get to the next one properly */
+ if(flags & FLAGS_WIDTHPARAM) {
+ width = (int)input[optr->width].val.nums;
if(width < 0) {
/* "A negative field width is taken as a '-' flag followed by a
positive field width." */
- width = -width;
- p->flags |= FLAGS_LEFT;
- p->flags &= ~FLAGS_PAD_NIL;
+ if(width == INT_MIN)
+ width = INT_MAX;
+ else
+ width = -width;
+ flags |= FLAGS_LEFT;
+ flags &= ~FLAGS_PAD_NIL;
}
}
else
- width = p->width;
+ width = optr->width;
/* pick up the specified precision */
- if(p->flags & FLAGS_PRECPARAM) {
- prec = (long)vto[p->precision].data.num.as_signed;
- param_num++; /* since the precision is extracted from a parameter, we
- must skip that to get to the next one properly */
+ if(flags & FLAGS_PRECPARAM) {
+ prec = (int)input[optr->precision].val.nums;
if(prec < 0)
/* "A negative precision is taken as if the precision were
omitted." */
prec = -1;
}
- else if(p->flags & FLAGS_PREC)
- prec = p->precision;
+ else if(flags & FLAGS_PREC)
+ prec = optr->precision;
else
prec = -1;
- is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
+ is_alt = (flags & FLAGS_ALT) ? 1 : 0;
+ iptr = &input[optr->input];
- switch(p->type) {
+ switch(iptr->type) {
+ case FORMAT_INTU:
+ case FORMAT_LONGU:
+ case FORMAT_LONGLONGU:
+ flags |= FLAGS_UNSIGNED;
+ FALLTHROUGH();
case FORMAT_INT:
- num = p->data.num.as_unsigned;
- if(p->flags & FLAGS_CHAR) {
+ case FORMAT_LONG:
+ case FORMAT_LONGLONG:
+ num = iptr->val.numu;
+ if(flags & FLAGS_CHAR) {
/* Character. */
- if(!(p->flags & FLAGS_LEFT))
+ if(!(flags & FLAGS_LEFT))
while(--width > 0)
OUTCHAR(' ');
OUTCHAR((char) num);
- if(p->flags & FLAGS_LEFT)
+ if(flags & FLAGS_LEFT)
while(--width > 0)
OUTCHAR(' ');
break;
}
- if(p->flags & FLAGS_OCTAL) {
- /* Octal unsigned integer. */
+ if(flags & FLAGS_OCTAL) {
+ /* Octal unsigned integer */
base = 8;
- goto unsigned_number;
+ is_neg = FALSE;
}
- else if(p->flags & FLAGS_HEX) {
- /* Hexadecimal unsigned integer. */
-
- digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+ else if(flags & FLAGS_HEX) {
+ /* Hexadecimal unsigned integer */
+ digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits;
base = 16;
- goto unsigned_number;
+ is_neg = FALSE;
}
- else if(p->flags & FLAGS_UNSIGNED) {
- /* Decimal unsigned integer. */
+ else if(flags & FLAGS_UNSIGNED) {
+ /* Decimal unsigned integer */
base = 10;
- goto unsigned_number;
+ is_neg = FALSE;
}
+ else {
+ /* Decimal integer. */
+ base = 10;
- /* Decimal integer. */
- base = 10;
-
- is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
- if(is_neg) {
- /* signed_num might fail to hold absolute negative minimum by 1 */
- signed_num = p->data.num.as_signed + (mp_intmax_t)1;
- signed_num = -signed_num;
- num = (mp_uintmax_t)signed_num;
- num += (mp_uintmax_t)1;
+ is_neg = (iptr->val.nums < (mp_intmax_t)0);
+ if(is_neg) {
+ /* signed_num might fail to hold absolute negative minimum by 1 */
+ signed_num = iptr->val.nums + (mp_intmax_t)1;
+ signed_num = -signed_num;
+ num = (mp_uintmax_t)signed_num;
+ num += (mp_uintmax_t)1;
+ }
}
-
- goto number;
-
-unsigned_number:
- /* Unsigned number of base BASE. */
- is_neg = 0;
-
number:
- /* Number of base BASE. */
-
/* Supply a default precision if none was given. */
if(prec == -1)
prec = 1;
/* Put the number in WORK. */
w = workend;
- while(num > 0) {
- *w-- = digits[num % base];
- num /= base;
+ switch(base) {
+ case 10:
+ while(num > 0) {
+ *w-- = (char)('0' + (num % 10));
+ num /= 10;
+ }
+ break;
+ default:
+ while(num > 0) {
+ *w-- = digits[num % base];
+ num /= base;
+ }
+ break;
}
- width -= (long)(workend - w);
- prec -= (long)(workend - w);
+ width -= (int)(workend - w);
+ prec -= (int)(workend - w);
if(is_alt && base == 8 && prec <= 0) {
*w-- = '0';
@@ -775,29 +829,29 @@ number:
if(is_alt && base == 16)
width -= 2;
- if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
+ if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
--width;
- if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
+ if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
while(width-- > 0)
OUTCHAR(' ');
if(is_neg)
OUTCHAR('-');
- else if(p->flags & FLAGS_SHOWSIGN)
+ else if(flags & FLAGS_SHOWSIGN)
OUTCHAR('+');
- else if(p->flags & FLAGS_SPACE)
+ else if(flags & FLAGS_SPACE)
OUTCHAR(' ');
if(is_alt && base == 16) {
OUTCHAR('0');
- if(p->flags & FLAGS_UPPER)
+ if(flags & FLAGS_UPPER)
OUTCHAR('X');
else
OUTCHAR('x');
}
- if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
+ if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
while(width-- > 0)
OUTCHAR('0');
@@ -806,219 +860,199 @@ number:
OUTCHAR(*w);
}
- if(p->flags & FLAGS_LEFT)
+ if(flags & FLAGS_LEFT)
while(width-- > 0)
OUTCHAR(' ');
break;
- case FORMAT_STRING:
- /* String. */
- {
- static const char null[] = "(nil)";
- const char *str;
- size_t len;
-
- str = (char *) p->data.str;
- if(!str) {
- /* Write null[] if there's space. */
- if(prec == -1 || prec >= (long) sizeof(null) - 1) {
- str = null;
- len = sizeof(null) - 1;
- /* Disable quotes around (nil) */
- p->flags &= (~FLAGS_ALT);
- }
- else {
- str = "";
- len = 0;
- }
+ case FORMAT_STRING: {
+ const char *str;
+ size_t len;
+
+ str = (char *)iptr->val.str;
+ if(!str) {
+ /* Write null string if there's space. */
+ if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
+ str = nilstr;
+ len = sizeof(nilstr) - 1;
+ /* Disable quotes around (nil) */
+ flags &= (~FLAGS_ALT);
}
- else if(prec != -1)
- len = (size_t)prec;
- else if(*str == '\0')
+ else {
+ str = "";
len = 0;
- else
- len = strlen(str);
+ }
+ }
+ else if(prec != -1)
+ len = (size_t)prec;
+ else if(*str == '\0')
+ len = 0;
+ else
+ len = strlen(str);
- width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
+ width -= (len > INT_MAX) ? INT_MAX : (int)len;
- if(p->flags & FLAGS_ALT)
- OUTCHAR('"');
+ if(flags & FLAGS_ALT)
+ OUTCHAR('"');
- if(!(p->flags&FLAGS_LEFT))
- while(width-- > 0)
- OUTCHAR(' ');
+ if(!(flags&FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
- for(; len && *str; len--)
- OUTCHAR(*str++);
- if(p->flags&FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
+ for(; len && *str; len--)
+ OUTCHAR(*str++);
+ if(flags&FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
- if(p->flags & FLAGS_ALT)
- OUTCHAR('"');
- }
+ if(flags & FLAGS_ALT)
+ OUTCHAR('"');
break;
+ }
case FORMAT_PTR:
/* Generic pointer. */
- {
- void *ptr;
- ptr = (void *) p->data.ptr;
- if(ptr) {
- /* If the pointer is not NULL, write it as a %#x spec. */
- base = 16;
- digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
- is_alt = 1;
- num = (size_t) ptr;
- is_neg = 0;
- goto number;
- }
- else {
- /* Write "(nil)" for a nil pointer. */
- static const char strnil[] = "(nil)";
- const char *point;
-
- width -= (long)(sizeof(strnil) - 1);
- if(p->flags & FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
- for(point = strnil; *point != '\0'; ++point)
- OUTCHAR(*point);
- if(!(p->flags & FLAGS_LEFT))
- while(width-- > 0)
- OUTCHAR(' ');
- }
+ if(iptr->val.ptr) {
+ /* If the pointer is not NULL, write it as a %#x spec. */
+ base = 16;
+ digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits;
+ is_alt = TRUE;
+ num = (size_t) iptr->val.ptr;
+ is_neg = FALSE;
+ goto number;
}
- break;
+ else {
+ /* Write "(nil)" for a nil pointer. */
+ const char *point;
- case FORMAT_DOUBLE:
- {
- char formatbuf[32]="%";
- char *fptr = &formatbuf[1];
- size_t left = sizeof(formatbuf)-strlen(formatbuf);
- int len;
-
- width = -1;
- if(p->flags & FLAGS_WIDTH)
- width = p->width;
- else if(p->flags & FLAGS_WIDTHPARAM)
- width = (long)vto[p->width].data.num.as_signed;
+ width -= (int)(sizeof(nilstr) - 1);
+ if(flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+ for(point = nilstr; *point != '\0'; ++point)
+ OUTCHAR(*point);
+ if(!(flags & FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
+ }
+ break;
- prec = -1;
- if(p->flags & FLAGS_PREC)
- prec = p->precision;
- else if(p->flags & FLAGS_PRECPARAM)
- prec = (long)vto[p->precision].data.num.as_signed;
-
- if(p->flags & FLAGS_LEFT)
- *fptr++ = '-';
- if(p->flags & FLAGS_SHOWSIGN)
- *fptr++ = '+';
- if(p->flags & FLAGS_SPACE)
- *fptr++ = ' ';
- if(p->flags & FLAGS_ALT)
- *fptr++ = '#';
-
- *fptr = 0;
-
- if(width >= 0) {
- if(width >= (long)sizeof(work))
- width = sizeof(work)-1;
- /* RECURSIVE USAGE */
- len = curl_msnprintf(fptr, left, "%ld", width);
- fptr += len;
- left -= len;
+ case FORMAT_DOUBLE: {
+ char formatbuf[32]="%";
+ char *fptr = &formatbuf[1];
+ size_t left = sizeof(formatbuf)-strlen(formatbuf);
+ int len;
+
+ if(flags & FLAGS_WIDTH)
+ width = optr->width;
+
+ if(flags & FLAGS_PREC)
+ prec = optr->precision;
+
+ if(flags & FLAGS_LEFT)
+ *fptr++ = '-';
+ if(flags & FLAGS_SHOWSIGN)
+ *fptr++ = '+';
+ if(flags & FLAGS_SPACE)
+ *fptr++ = ' ';
+ if(flags & FLAGS_ALT)
+ *fptr++ = '#';
+
+ *fptr = 0;
+
+ if(width >= 0) {
+ if(width >= (int)sizeof(work))
+ width = sizeof(work)-1;
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, "%d", width);
+ fptr += len;
+ left -= len;
+ }
+ if(prec >= 0) {
+ /* for each digit in the integer part, we can have one less
+ precision */
+ size_t maxprec = sizeof(work) - 2;
+ double val = iptr->val.dnum;
+ if(width > 0 && prec <= width)
+ maxprec -= width;
+ while(val >= 10.0) {
+ val /= 10;
+ maxprec--;
}
- if(prec >= 0) {
- /* for each digit in the integer part, we can have one less
- precision */
- size_t maxprec = sizeof(work) - 2;
- double val = p->data.dnum;
- if(width > 0 && prec <= width)
- maxprec -= width;
- while(val >= 10.0) {
- val /= 10;
- maxprec--;
- }
- if(prec > (long)maxprec)
- prec = (long)maxprec-1;
- if(prec < 0)
- prec = 0;
- /* RECURSIVE USAGE */
- len = curl_msnprintf(fptr, left, ".%ld", prec);
- fptr += len;
- }
- if(p->flags & FLAGS_LONG)
- *fptr++ = 'l';
+ if(prec > (int)maxprec)
+ prec = (int)maxprec-1;
+ if(prec < 0)
+ prec = 0;
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, ".%d", prec);
+ fptr += len;
+ }
+ if(flags & FLAGS_LONG)
+ *fptr++ = 'l';
- if(p->flags & FLAGS_FLOATE)
- *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
- else if(p->flags & FLAGS_FLOATG)
- *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
- else
- *fptr++ = 'f';
+ if(flags & FLAGS_FLOATE)
+ *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E':'e');
+ else if(flags & FLAGS_FLOATG)
+ *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
+ else
+ *fptr++ = 'f';
- *fptr = 0; /* and a final null-termination */
+ *fptr = 0; /* and a final null-termination */
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
- /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
- output characters */
+ /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
+ output characters */
#ifdef HAVE_SNPRINTF
- (snprintf)(work, sizeof(work), formatbuf, p->data.dnum);
+ (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum);
#else
- (sprintf)(work, formatbuf, p->data.dnum);
+ (sprintf)(work, formatbuf, iptr->val.dnum);
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
- DEBUGASSERT(strlen(work) <= sizeof(work));
- for(fptr = work; *fptr; fptr++)
- OUTCHAR(*fptr);
- }
+ DEBUGASSERT(strlen(work) <= sizeof(work));
+ for(fptr = work; *fptr; fptr++)
+ OUTCHAR(*fptr);
break;
+ }
case FORMAT_INTPTR:
/* Answer the count of characters written. */
#ifdef HAVE_LONG_LONG_TYPE
- if(p->flags & FLAGS_LONGLONG)
- *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
+ if(flags & FLAGS_LONGLONG)
+ *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done;
else
#endif
- if(p->flags & FLAGS_LONG)
- *(long *) p->data.ptr = (long)done;
- else if(!(p->flags & FLAGS_SHORT))
- *(int *) p->data.ptr = (int)done;
+ if(flags & FLAGS_LONG)
+ *(long *) iptr->val.ptr = (long)done;
+ else if(!(flags & FLAGS_SHORT))
+ *(int *) iptr->val.ptr = (int)done;
else
- *(short *) p->data.ptr = (short)done;
+ *(short *) iptr->val.ptr = (short)done;
break;
default:
break;
}
- f = *end++; /* goto end of %-code */
-
}
return done;
}
/* fputc() look-alike */
-static int addbyter(int output, FILE *data)
+static int addbyter(unsigned char outc, void *f)
{
- struct nsprintf *infop = (struct nsprintf *)data;
- unsigned char outc = (unsigned char)output;
-
+ struct nsprintf *infop = f;
if(infop->length < infop->max) {
/* only do this if we haven't reached max length yet */
- infop->buffer[0] = outc; /* store */
- infop->buffer++; /* increase pointer */
+ *infop->buffer++ = outc; /* store */
infop->length++; /* we are now one byte larger */
- return outc; /* fputc() returns like this on success */
+ return 0; /* fputc() returns like this on success */
}
- return -1;
+ return 1;
}
int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
@@ -1031,7 +1065,7 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
info.length = 0;
info.max = maxlength;
- retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+ retcode = formatf(&info, addbyter, format, ap_save);
if(info.max) {
/* we terminate this with a zero byte */
if(info.max == info.length) {
@@ -1057,29 +1091,28 @@ int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
}
/* fputc() look-alike */
-static int alloc_addbyter(int output, FILE *data)
+static int alloc_addbyter(unsigned char outc, void *f)
{
- struct asprintf *infop = (struct asprintf *)data;
- unsigned char outc = (unsigned char)output;
-
- if(Curl_dyn_addn(infop->b, &outc, 1)) {
- infop->fail = 1;
- return -1; /* fail */
+ struct asprintf *infop = f;
+ CURLcode result = Curl_dyn_addn(infop->b, &outc, 1);
+ if(result) {
+ infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
+ return 1 ; /* fail */
}
- return outc; /* fputc() returns like this on success */
+ return 0;
}
-/* appends the formatted string, returns 0 on success, 1 on error */
+/* appends the formatted string, returns MERR error code */
int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
{
struct asprintf info;
info.b = dyn;
- info.fail = 0;
+ info.merr = MERR_OK;
- (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
- if(info.fail) {
+ (void)formatf(&info, alloc_addbyter, format, ap_save);
+ if(info.merr) {
Curl_dyn_free(info.b);
- return 1;
+ return info.merr;
}
return 0;
}
@@ -1090,10 +1123,10 @@ char *curl_mvaprintf(const char *format, va_list ap_save)
struct dynbuf dyn;
info.b = &dyn;
Curl_dyn_init(info.b, DYN_APRINTF);
- info.fail = 0;
+ info.merr = MERR_OK;
- (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
- if(info.fail) {
+ (void)formatf(&info, alloc_addbyter, format, ap_save);
+ if(info.merr) {
Curl_dyn_free(info.b);
return NULL;
}
@@ -1112,13 +1145,12 @@ char *curl_maprintf(const char *format, ...)
return s;
}
-static int storebuffer(int output, FILE *data)
+static int storebuffer(unsigned char outc, void *f)
{
- char **buffer = (char **)data;
- unsigned char outc = (unsigned char)output;
+ char **buffer = f;
**buffer = outc;
(*buffer)++;
- return outc; /* act like fputc() ! */
+ return 0;
}
int curl_msprintf(char *buffer, const char *format, ...)
@@ -1126,19 +1158,29 @@ int curl_msprintf(char *buffer, const char *format, ...)
va_list ap_save; /* argument pointer */
int retcode;
va_start(ap_save, format);
- retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ retcode = formatf(&buffer, storebuffer, format, ap_save);
va_end(ap_save);
*buffer = 0; /* we terminate this with a zero byte */
return retcode;
}
+static int fputc_wrapper(unsigned char outc, void *f)
+{
+ int out = outc;
+ FILE *s = f;
+ int rc = fputc(out, s);
+ if(rc == out)
+ return 0;
+ return 1;
+}
+
int curl_mprintf(const char *format, ...)
{
int retcode;
va_list ap_save; /* argument pointer */
va_start(ap_save, format);
- retcode = dprintf_formatf(stdout, fputc, format, ap_save);
+ retcode = formatf(stdout, fputc_wrapper, format, ap_save);
va_end(ap_save);
return retcode;
}
@@ -1148,25 +1190,24 @@ int curl_mfprintf(FILE *whereto, const char *format, ...)
int retcode;
va_list ap_save; /* argument pointer */
va_start(ap_save, format);
- retcode = dprintf_formatf(whereto, fputc, format, ap_save);
+ retcode = formatf(whereto, fputc_wrapper, format, ap_save);
va_end(ap_save);
return retcode;
}
int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
{
- int retcode;
- retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ int retcode = formatf(&buffer, storebuffer, format, ap_save);
*buffer = 0; /* we terminate this with a zero byte */
return retcode;
}
int curl_mvprintf(const char *format, va_list ap_save)
{
- return dprintf_formatf(stdout, fputc, format, ap_save);
+ return formatf(stdout, fputc_wrapper, format, ap_save);
}
int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
{
- return dprintf_formatf(whereto, fputc, format, ap_save);
+ return formatf(whereto, fputc_wrapper, format, ap_save);
}
diff --git a/libs/libcurl/src/mqtt.c b/libs/libcurl/src/mqtt.c
index 530d929e5e..20b839159b 100644
--- a/libs/libcurl/src/mqtt.c
+++ b/libs/libcurl/src/mqtt.c
@@ -88,7 +88,7 @@ const struct Curl_handler Curl_handler_mqtt = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_MQTT, /* defport */
@@ -524,8 +524,10 @@ static CURLcode mqtt_publish(struct Curl_easy *data)
char encodedbytes[4];
curl_off_t postfieldsize = data->set.postfieldsize;
- if(!payload)
+ if(!payload) {
+ DEBUGF(infof(data, "mqtt_publish without payload, return bad arg"));
return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
if(postfieldsize < 0)
payloadlen = strlen(payload);
else
@@ -622,7 +624,6 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
struct connectdata *conn = data->conn;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
ssize_t nread;
- unsigned char *pkt = (unsigned char *)data->state.buffer;
size_t remlen;
struct mqtt_conn *mqtt = &conn->proto.mqtt;
struct MQTT *mq = data->req.p.mqtt;
@@ -671,13 +672,14 @@ MQTT_SUBACK_COMING:
data->req.bytecount = 0;
data->req.size = remlen;
mq->npacket = remlen; /* get this many bytes */
- /* FALLTHROUGH */
+ FALLTHROUGH();
case MQTT_PUB_REMAIN: {
/* read rest of packet, but no more. Cap to buffer size */
+ char buffer[4*1024];
size_t rest = mq->npacket;
- if(rest > (size_t)data->set.buffer_size)
- rest = (size_t)data->set.buffer_size;
- result = Curl_read(data, sockfd, (char *)pkt, rest, &nread);
+ if(rest > sizeof(buffer))
+ rest = sizeof(buffer);
+ result = Curl_read(data, sockfd, buffer, rest, &nread);
if(result) {
if(CURLE_AGAIN == result) {
infof(data, "EEEE AAAAGAIN");
@@ -690,14 +692,12 @@ MQTT_SUBACK_COMING:
goto end;
}
- mq->npacket -= nread;
-
/* if QoS is set, message contains packet id */
-
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)pkt, nread);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
if(result)
goto end;
+ mq->npacket -= nread;
if(!mq->npacket)
/* no more PUBLISH payload, back to subscribe wait state */
mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
@@ -745,7 +745,6 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
struct MQTT *mq = data->req.p.mqtt;
ssize_t nread;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
- unsigned char *pkt = (unsigned char *)data->state.buffer;
unsigned char byte;
*done = FALSE;
@@ -776,14 +775,14 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
/* remember the first byte */
mq->npacket = 0;
mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case MQTT_REMAINING_LENGTH:
do {
result = Curl_read(data, sockfd, (char *)&byte, 1, &nread);
if(!nread)
break;
Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
- pkt[mq->npacket++] = byte;
+ mq->pkt_hd[mq->npacket++] = byte;
} while((byte & 0x80) && (mq->npacket < 4));
if(nread && (byte & 0x80))
/* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
@@ -791,7 +790,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
result = CURLE_WEIRD_SERVER_REPLY;
if(result)
break;
- mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL);
+ mq->remaining_length = mqtt_decode_len(mq->pkt_hd, mq->npacket, NULL);
mq->npacket = 0;
if(mq->remaining_length) {
mqstate(data, mqtt->nextstate, MQTT_NOSTATE);
diff --git a/libs/libcurl/src/mqtt.h b/libs/libcurl/src/mqtt.h
index bb768268f1..ea81f40448 100644
--- a/libs/libcurl/src/mqtt.h
+++ b/libs/libcurl/src/mqtt.h
@@ -57,6 +57,7 @@ struct MQTT {
unsigned char firstbyte;
size_t remaining_length;
struct dynbuf recvbuf;
+ unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
};
#endif /* HEADER_CURL_MQTT_H */
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c
index 8e6f2c739d..1ff096b625 100644
--- a/libs/libcurl/src/multi.c
+++ b/libs/libcurl/src/multi.c
@@ -672,6 +672,7 @@ static CURLcode multi_done(struct Curl_easy *data,
many callbacks and protocols work differently, we could potentially do
this more fine-grained in the future. */
premature = TRUE;
+ FALLTHROUGH();
default:
break;
}
@@ -993,31 +994,92 @@ void Curl_attach_connection(struct Curl_easy *data,
Curl_conn_ev_data_attach(conn, data);
}
-static int domore_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks)
+static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
+ struct connectdata *conn = data->conn;
+ (void)socks;
+ /* Not using `conn->sockfd` as `Curl_setup_transfer()` initializes
+ * that *after* the connect. */
+ if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
+ /* Default is to wait to something from the server */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0);
+ }
+ return GETSOCK_BLANK;
+}
+
+static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+ struct connectdata *conn = data->conn;
+ if(conn && conn->handler->proto_getsock)
+ return conn->handler->proto_getsock(data, conn, socks);
+ else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ /* Default is to wait to something from the server */
+ socks[0] = conn->sockfd;
+ return GETSOCK_READSOCK(0);
+ }
+ return GETSOCK_BLANK;
+}
+
+static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+ struct connectdata *conn = data->conn;
if(conn && conn->handler->domore_getsock)
return conn->handler->domore_getsock(data, conn, socks);
+ else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ /* Default is that we want to send something to the server */
+ socks[0] = conn->sockfd;
+ return GETSOCK_WRITESOCK(0);
+ }
return GETSOCK_BLANK;
}
-static int doing_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks)
+static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
+ struct connectdata *conn = data->conn;
if(conn && conn->handler->doing_getsock)
return conn->handler->doing_getsock(data, conn, socks);
+ else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+ /* Default is that we want to send something to the server */
+ socks[0] = conn->sockfd;
+ return GETSOCK_WRITESOCK(0);
+ }
return GETSOCK_BLANK;
}
-static int protocol_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks)
+static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
{
- if(conn->handler->proto_getsock)
- return conn->handler->proto_getsock(data, conn, socks);
- return GETSOCK_BLANK;
+ struct connectdata *conn = data->conn;
+
+ if(!conn)
+ return GETSOCK_BLANK;
+ else if(conn->handler->perform_getsock)
+ return conn->handler->perform_getsock(data, conn, sock);
+ else {
+ /* Default is to obey the data->req.keepon flags for send/recv */
+ int bitmap = GETSOCK_BLANK;
+ unsigned sockindex = 0;
+ if(CURL_WANT_RECV(data)) {
+ DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
+ bitmap |= GETSOCK_READSOCK(sockindex);
+ sock[sockindex] = conn->sockfd;
+ }
+
+ if(CURL_WANT_SEND(data)) {
+ if((conn->sockfd != conn->writesockfd) ||
+ bitmap == GETSOCK_BLANK) {
+ /* only if they are not the same socket and we have a readable
+ one, we increase index */
+ if(bitmap != GETSOCK_BLANK)
+ sockindex++; /* increase index if we need two entries */
+
+ DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
+ sock[sockindex] = conn->writesockfd;
+ }
+ bitmap |= GETSOCK_WRITESOCK(sockindex);
+ }
+ return bitmap;
+ }
}
/* Initializes `poll_set` with the current socket poll actions needed
@@ -1033,45 +1095,61 @@ static void multi_getsock(struct Curl_easy *data,
return;
switch(data->mstate) {
- default:
+ case MSTATE_INIT:
+ case MSTATE_PENDING:
+ case MSTATE_CONNECT:
+ /* nothing to poll for yet */
break;
case MSTATE_RESOLVING:
- Curl_pollset_add_socks2(data, ps, Curl_resolv_getsock);
+ Curl_pollset_add_socks(data, ps, Curl_resolv_getsock);
/* connection filters are not involved in this phase */
- return;
+ break;
+
+ case MSTATE_CONNECTING:
+ case MSTATE_TUNNELING:
+ Curl_pollset_add_socks(data, ps, connecting_getsock);
+ Curl_conn_adjust_pollset(data, ps);
+ break;
- case MSTATE_PROTOCONNECTING:
case MSTATE_PROTOCONNECT:
+ case MSTATE_PROTOCONNECTING:
Curl_pollset_add_socks(data, ps, protocol_getsock);
+ Curl_conn_adjust_pollset(data, ps);
break;
case MSTATE_DO:
case MSTATE_DOING:
Curl_pollset_add_socks(data, ps, doing_getsock);
- break;
-
- case MSTATE_TUNNELING:
- case MSTATE_CONNECTING:
+ Curl_conn_adjust_pollset(data, ps);
break;
case MSTATE_DOING_MORE:
Curl_pollset_add_socks(data, ps, domore_getsock);
+ Curl_conn_adjust_pollset(data, ps);
break;
- case MSTATE_DID: /* since is set after DO is completed, we switch to
- waiting for the same as the PERFORMING state */
+ case MSTATE_DID: /* same as PERFORMING in regard to polling */
case MSTATE_PERFORMING:
- Curl_pollset_add_socks(data, ps, Curl_single_getsock);
+ Curl_pollset_add_socks(data, ps, perform_getsock);
+ Curl_conn_adjust_pollset(data, ps);
break;
case MSTATE_RATELIMITING:
- /* nothing to wait for */
- return;
- }
+ /* we need to let time pass, ignore socket(s) */
+ break;
+
+ case MSTATE_DONE:
+ case MSTATE_COMPLETED:
+ case MSTATE_MSGSENT:
+ /* nothing more to poll for */
+ break;
- /* Let connection filters add/remove as needed */
- Curl_conn_adjust_pollset(data, ps);
+ default:
+ failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
+ DEBUGASSERT(0);
+ break;
+ }
}
CURLMcode curl_multi_fdset(struct Curl_multi *multi,
@@ -1942,6 +2020,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
if(!result) {
+ *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(async)
/* We're now waiting for an asynchronous name lookup */
multistate(data, MSTATE_RESOLVING);
@@ -1983,8 +2062,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(dns) {
#ifdef CURLRES_ASYNCH
- conn->resolve_async.dns = dns;
- conn->resolve_async.done = TRUE;
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
#endif
result = CURLE_OK;
infof(data, "Hostname '%s' was found in DNS cache", hostname);
@@ -2371,7 +2450,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
{
char *newurl = NULL;
bool retry = FALSE;
- bool comeback = FALSE;
DEBUGASSERT(data->state.buffer);
/* check if over send speed */
send_timeout_ms = 0;
@@ -2402,7 +2480,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* read/write data if it is ready to do so */
- result = Curl_readwrite(data->conn, data, &done, &comeback);
+ result = Curl_readwrite(data, &done);
if(done || (result == CURLE_RECV_ERROR)) {
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
@@ -2512,7 +2590,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
}
}
- else if(comeback) {
+ else if(data->state.select_bits) {
/* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer
won't get stuck on this transfer at the expense of other concurrent
transfers */
@@ -3164,7 +3242,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
/* set socket event bitmask if they're not locked */
- data->conn->cselect_bits = (unsigned char)ev_bitmask;
+ data->state.select_bits = (unsigned char)ev_bitmask;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
diff --git a/libs/libcurl/src/noproxy.c b/libs/libcurl/src/noproxy.c
index 8517cbf343..db372732f5 100644
--- a/libs/libcurl/src/noproxy.c
+++ b/libs/libcurl/src/noproxy.c
@@ -216,7 +216,6 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy,
/* case C passes through, not a match */
break;
case TYPE_IPV4:
- /* FALLTHROUGH */
case TYPE_IPV6: {
const char *check = token;
char *slash;
diff --git a/libs/libcurl/src/openldap.c b/libs/libcurl/src/openldap.c
index bd5a5f88c8..7452981562 100644
--- a/libs/libcurl/src/openldap.c
+++ b/libs/libcurl/src/openldap.c
@@ -130,7 +130,7 @@ const struct Curl_handler Curl_handler_ldap = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
oldap_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAP, /* defport */
@@ -158,7 +158,7 @@ const struct Curl_handler Curl_handler_ldaps = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
oldap_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_LDAPS, /* defport */
@@ -645,7 +645,7 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
switch(code) {
case LDAP_SIZELIMIT_EXCEEDED:
infof(data, "Too many authentication mechanisms\n");
- /* FALLTHROUGH */
+ FALLTHROUGH();
case LDAP_SUCCESS:
case LDAP_NO_RESULTS_RETURNED:
if(Curl_sasl_can_authenticate(&li->sasl, data))
@@ -793,10 +793,13 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
result = oldap_perform_bind(data, OLDAP_BIND);
break;
}
- /* FALLTHROUGH */
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result)
+ break;
+ FALLTHROUGH();
case OLDAP_TLS:
result = oldap_ssl_connect(data, OLDAP_TLS);
- if(result && data->set.use_ssl != CURLUSESSL_TRY)
+ if(result)
result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
else if(ssl_installed(conn)) {
conn->bits.tls_upgraded = TRUE;
@@ -887,10 +890,14 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
result = oldap_url_parse(data, &lud);
if(!result) {
- Sockbuf *sb;
- /* re-install the libcurl SSL handlers into the sockbuf. */
- ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
- ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+#ifdef USE_SSL
+ if(ssl_installed(conn)) {
+ Sockbuf *sb;
+ /* re-install the libcurl SSL handlers into the sockbuf. */
+ ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+ ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+ }
+#endif
rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
lud->lud_filter, lud->lud_attrs, 0,
@@ -1014,7 +1021,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
switch(code) {
case LDAP_SIZELIMIT_EXCEEDED:
infof(data, "There are more than %d entries", lr->nument);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case LDAP_SUCCESS:
data->req.size = data->req.bytecount;
break;
diff --git a/libs/libcurl/src/pingpong.c b/libs/libcurl/src/pingpong.c
index be892a866d..71116c8a9c 100644
--- a/libs/libcurl/src/pingpong.c
+++ b/libs/libcurl/src/pingpong.c
@@ -36,6 +36,7 @@
#include "pingpong.h"
#include "multiif.h"
#include "vtls/vtls.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -105,7 +106,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
if(Curl_conn_data_pending(data, FIRSTSOCKET))
rc = 1;
- else if(Curl_pp_moredata(pp))
+ else if(pp->overflow)
/* We are receiving and there is data in the cache so just read it */
rc = 1;
else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET))
@@ -139,19 +140,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
}
/* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp)
+void Curl_pp_init(struct pingpong *pp)
{
- DEBUGASSERT(data);
pp->nread_resp = 0;
- pp->linestart_resp = data->state.buffer;
- pp->pending_resp = TRUE;
pp->response = Curl_now(); /* start response time-out now! */
-}
-
-/* setup for the coming transfer */
-void Curl_pp_setup(struct pingpong *pp)
-{
+ pp->pending_resp = TRUE;
Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
+ Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
}
/***********************************************************************
@@ -197,9 +192,9 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
if(result)
return result;
+ pp->pending_resp = TRUE;
write_len = Curl_dyn_len(&pp->sendbuf);
s = Curl_dyn_ptr(&pp->sendbuf);
- Curl_pp_init(data, pp);
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CMD;
@@ -255,6 +250,25 @@ CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp,
return result;
}
+static CURLcode pingpong_read(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ char *buffer,
+ size_t buflen,
+ ssize_t *nread)
+{
+ CURLcode result;
+#ifdef HAVE_GSSAPI
+ enum protection_level prot = data->conn->data_prot;
+ data->conn->data_prot = PROT_CLEAR;
+#endif
+ result = Curl_read(data, sockfd, buffer, buflen, nread);
+#ifdef HAVE_GSSAPI
+ DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
+ data->conn->data_prot = (unsigned char)prot;
+#endif
+ return result;
+}
+
/*
* Curl_pp_readresp()
*
@@ -266,181 +280,96 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
int *code, /* return the server code if done */
size_t *size) /* size of the response */
{
- ssize_t perline; /* count bytes per line */
- bool keepon = TRUE;
- ssize_t gotbytes;
- char *ptr;
struct connectdata *conn = data->conn;
- char * const buf = data->state.buffer;
CURLcode result = CURLE_OK;
*code = 0; /* 0 for errors or not done */
*size = 0;
- ptr = buf + pp->nread_resp;
+ if(pp->nfinal) {
+ /* a previous call left this many bytes in the beginning of the buffer as
+ that was the final line; now ditch that */
+ size_t full = Curl_dyn_len(&pp->recvbuf);
- /* number of bytes in the current line, so far */
- perline = (ssize_t)(ptr-pp->linestart_resp);
+ /* trim off the "final" leading part */
+ Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal);
- while((pp->nread_resp < (size_t)data->set.buffer_size) &&
- (keepon && !result)) {
+ pp->nfinal = 0; /* now gone */
+ }
+ if(!pp->overflow) {
+ ssize_t gotbytes = 0;
+ char buffer[900];
- if(pp->cache) {
- /* we had data in the "cache", copy that instead of doing an actual
- * read
- *
- * pp->cache_size is cast to ssize_t here. This should be safe, because
- * it would have been populated with something of size int to begin
- * with, even though its datatype may be larger than an int.
- */
- if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) {
- failf(data, "cached response data too big to handle");
- return CURLE_WEIRD_SERVER_REPLY;
- }
- memcpy(ptr, pp->cache, pp->cache_size);
- gotbytes = (ssize_t)pp->cache_size;
- free(pp->cache); /* free the cache */
- pp->cache = NULL; /* clear the pointer */
- pp->cache_size = 0; /* zero the size just in case */
- }
- else {
-#ifdef HAVE_GSSAPI
- enum protection_level prot = conn->data_prot;
- conn->data_prot = PROT_CLEAR;
-#endif
- DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
- (buf + data->set.buffer_size + 1));
- result = Curl_read(data, sockfd, ptr,
- data->set.buffer_size - pp->nread_resp,
- &gotbytes);
-#ifdef HAVE_GSSAPI
- DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
- conn->data_prot = (unsigned char)prot;
-#endif
- if(result == CURLE_AGAIN)
- return CURLE_OK; /* return */
+ result = pingpong_read(data, sockfd, buffer, sizeof(buffer), &gotbytes);
+ if(result == CURLE_AGAIN)
+ return CURLE_OK;
- if(result)
- /* Set outer result variable to this error. */
- keepon = FALSE;
- }
+ if(result)
+ return result;
- if(!keepon)
- ;
- else if(gotbytes <= 0) {
- keepon = FALSE;
- result = CURLE_RECV_ERROR;
+ if(gotbytes <= 0) {
failf(data, "response reading failed (errno: %d)", SOCKERRNO);
+ return CURLE_RECV_ERROR;
}
- else {
- /* we got a whole chunk of data, which can be anything from one
- * byte to a set of lines and possible just a piece of the last
- * line */
- ssize_t i;
- ssize_t clipamount = 0;
- bool restart = FALSE;
-
- data->req.headerbytecount += (unsigned int)gotbytes;
-
- pp->nread_resp += gotbytes;
- for(i = 0; i < gotbytes; ptr++, i++) {
- perline++;
- if(*ptr == '\n') {
- /* a newline is CRLF in pp-talk, so the CR is ignored as
- the line isn't really terminated until the LF comes */
-
- /* output debug output if that is requested */
+
+ result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
+ if(result)
+ return result;
+
+ data->req.headerbytecount += (unsigned int)gotbytes;
+
+ pp->nread_resp += gotbytes;
+ }
+
+ do {
+ char *line = Curl_dyn_ptr(&pp->recvbuf);
+ char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
+ if(nl) {
+ /* a newline is CRLF in pp-talk, so the CR is ignored as
+ the line isn't really terminated until the LF comes */
+ size_t length = nl - line + 1;
+
+ /* output debug output if that is requested */
#ifdef HAVE_GSSAPI
- if(!conn->sec_complete)
+ if(!conn->sec_complete)
#endif
- Curl_debug(data, CURLINFO_HEADER_IN,
- pp->linestart_resp, (size_t)perline);
-
- /*
- * We pass all response-lines to the callback function registered
- * for "headers". The response lines can be seen as a kind of
- * headers.
- */
- result = Curl_client_write(data, CLIENTWRITE_INFO,
- pp->linestart_resp, perline);
- if(result)
- return result;
-
- if(pp->endofresp(data, conn, pp->linestart_resp, perline, code)) {
- /* This is the end of the last line, copy the last line to the
- start of the buffer and null-terminate, for old times sake */
- size_t n = ptr - pp->linestart_resp;
- memmove(buf, pp->linestart_resp, n);
- buf[n] = 0; /* null-terminate */
- keepon = FALSE;
- pp->linestart_resp = ptr + 1; /* advance pointer */
- i++; /* skip this before getting out */
-
- *size = pp->nread_resp; /* size of the response */
- pp->nread_resp = 0; /* restart */
- break;
- }
- perline = 0; /* line starts over here */
- pp->linestart_resp = ptr + 1;
- }
- }
+ Curl_debug(data, CURLINFO_HEADER_IN, line, length);
- if(!keepon && (i != gotbytes)) {
- /* We found the end of the response lines, but we didn't parse the
- full chunk of data we have read from the server. We therefore need
- to store the rest of the data to be checked on the next invoke as
- it may actually contain another end of response already! */
- clipamount = gotbytes - i;
- restart = TRUE;
- DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
- "server response left",
- (int)clipamount));
- }
- else if(keepon) {
-
- if((perline == gotbytes) &&
- (gotbytes > (ssize_t)data->set.buffer_size/2)) {
- /* We got an excessive line without newlines and we need to deal
- with it. We keep the first bytes of the line then we throw
- away the rest. */
- infof(data, "Excessive server response line length received, "
- "%zd bytes. Stripping", gotbytes);
- restart = TRUE;
-
- /* we keep 40 bytes since all our pingpong protocols are only
- interested in the first piece */
- clipamount = 40;
- }
- else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
- /* We got a large chunk of data and there's potentially still
- trailing data to take care of, so we put any such part in the
- "cache", clear the buffer to make space and restart. */
- clipamount = perline;
- restart = TRUE;
- }
- }
- else if(i == gotbytes)
- restart = TRUE;
-
- if(clipamount) {
- pp->cache_size = clipamount;
- pp->cache = malloc(pp->cache_size);
- if(pp->cache)
- memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
+ /*
+ * Pass all response-lines to the callback function registered for
+ * "headers". The response lines can be seen as a kind of headers.
+ */
+ result = Curl_client_write(data, CLIENTWRITE_INFO, line, length);
+ if(result)
+ return result;
+
+ if(pp->endofresp(data, conn, line, length, code)) {
+ /* When at "end of response", keep the endofresp line first in the
+ buffer since it will be accessed outside (by pingpong
+ parsers). Store the overflow counter to inform about additional
+ data in this buffer after the endofresp line. */
+ pp->nfinal = length;
+ if(Curl_dyn_len(&pp->recvbuf) > length)
+ pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
else
- return CURLE_OUT_OF_MEMORY;
+ pp->overflow = 0;
+ *size = pp->nread_resp; /* size of the response */
+ pp->nread_resp = 0; /* restart */
+ break;
}
- if(restart) {
- /* now reset a few variables to start over nicely from the start of
- the big buffer */
- pp->nread_resp = 0; /* start over from scratch in the buffer */
- ptr = pp->linestart_resp = buf;
- perline = 0;
- }
-
- } /* there was data */
+ if(Curl_dyn_len(&pp->recvbuf) > length)
+ /* keep the remaining piece */
+ Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
+ else
+ Curl_dyn_reset(&pp->recvbuf);
+ }
+ else {
+ /* without a newline, there is no overflow */
+ pp->overflow = 0;
+ break;
+ }
- } /* while there's buffer left and loop is requested */
+ } while(1); /* while there's buffer left to scan */
pp->pending_resp = FALSE;
@@ -488,14 +417,13 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
CURLcode Curl_pp_disconnect(struct pingpong *pp)
{
Curl_dyn_free(&pp->sendbuf);
- Curl_safefree(pp->cache);
+ Curl_dyn_free(&pp->recvbuf);
return CURLE_OK;
}
bool Curl_pp_moredata(struct pingpong *pp)
{
- return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
- TRUE : FALSE;
+ return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf));
}
#endif
diff --git a/libs/libcurl/src/pingpong.h b/libs/libcurl/src/pingpong.h
index ae2dbad325..a74071675c 100644
--- a/libs/libcurl/src/pingpong.h
+++ b/libs/libcurl/src/pingpong.h
@@ -47,16 +47,11 @@ typedef enum {
* It holds response cache and non-blocking sending data.
*/
struct pingpong {
- char *cache; /* data cache between getresponse()-calls */
- size_t cache_size; /* size of cache in bytes */
size_t nread_resp; /* number of bytes currently read of a server response */
- char *linestart_resp; /* line start pointer for the server response
- reader function */
bool pending_resp; /* set TRUE when a server response is pending or in
progress, and is cleared once the last response is
read */
- char *sendthis; /* allocated pointer to a buffer that is to be sent to the
- server */
+ char *sendthis; /* pointer to a buffer that is to be sent to the server */
size_t sendleft; /* number of bytes left to send from the sendthis buffer */
size_t sendsize; /* total size of the sendthis buffer */
struct curltime response; /* set to Curl_now() when a command has been sent
@@ -64,6 +59,10 @@ struct pingpong {
timediff_t response_time; /* When no timeout is given, this is the amount of
milliseconds we await for a server response. */
struct dynbuf sendbuf;
+ struct dynbuf recvbuf;
+ size_t overflow; /* number of bytes left after a final response line */
+ size_t nfinal; /* number of bytes in the final response line, which
+ after a match is first in the receice buffer */
/* Function pointers the protocols MUST implement and provide for the
pingpong layer to function */
@@ -90,10 +89,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp,
bool block, bool disconnecting);
/* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp);
-
-/* setup for the transfer */
-void Curl_pp_setup(struct pingpong *pp);
+void Curl_pp_init(struct pingpong *pp);
/* Returns timeout in ms. 0 or negative number means the timeout has already
triggered */
@@ -113,7 +109,7 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
*/
CURLcode Curl_pp_sendf(struct Curl_easy *data,
struct pingpong *pp,
- const char *fmt, ...);
+ const char *fmt, ...) CURL_PRINTF(3, 4);
/***********************************************************************
*
@@ -128,7 +124,7 @@ CURLcode Curl_pp_sendf(struct Curl_easy *data,
CURLcode Curl_pp_vsendf(struct Curl_easy *data,
struct pingpong *pp,
const char *fmt,
- va_list args);
+ va_list args) CURL_PRINTF(3, 0);
/*
* Curl_pp_readresp()
diff --git a/libs/libcurl/src/pop3.c b/libs/libcurl/src/pop3.c
index b6ad724c10..f31e86fe31 100644
--- a/libs/libcurl/src/pop3.c
+++ b/libs/libcurl/src/pop3.c
@@ -77,6 +77,7 @@
#include "curl_sasl.h"
#include "curl_md5.h"
#include "warnless.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -124,7 +125,7 @@ const struct Curl_handler Curl_handler_pop3 = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_POP3, /* defport */
@@ -153,7 +154,7 @@ const struct Curl_handler Curl_handler_pop3s = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_POP3S, /* defport */
@@ -251,8 +252,8 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = data->state.buffer;
- size_t len = strlen(message);
+ char *message = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+ size_t len = data->conn->proto.pop3c.pp.nfinal;
if(len > 2) {
/* Find the start of the message */
@@ -648,8 +649,8 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct pop3_conn *pop3c = &conn->proto.pop3c;
- const char *line = data->state.buffer;
- size_t len = strlen(line);
+ const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+ size_t len = data->conn->proto.pop3c.pp.nfinal;
(void)instate; /* no use for this yet */
@@ -657,44 +658,35 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
failf(data, "Got unexpected pop3-server response");
result = CURLE_WEIRD_SERVER_REPLY;
}
- else {
+ else if(len > 3) {
/* Does the server support APOP authentication? */
- if(len >= 4 && line[len - 2] == '>') {
- /* Look for the APOP timestamp */
- size_t i;
- for(i = 3; i < len - 2; ++i) {
- if(line[i] == '<') {
- /* Calculate the length of the timestamp */
- size_t timestamplen = len - 1 - i;
- char *at;
- if(!timestamplen)
- break;
-
- /* Allocate some memory for the timestamp */
- pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
-
- if(!pop3c->apoptimestamp)
- break;
-
- /* Copy the timestamp */
- memcpy(pop3c->apoptimestamp, line + i, timestamplen);
- pop3c->apoptimestamp[timestamplen] = '\0';
-
- /* If the timestamp does not contain '@' it is not (as required by
- RFC-1939) conformant to the RFC-822 message id syntax, and we
- therefore do not use APOP authentication. */
- at = strchr(pop3c->apoptimestamp, '@');
- if(!at)
- Curl_safefree(pop3c->apoptimestamp);
- else
- /* Store the APOP capability */
- pop3c->authtypes |= POP3_TYPE_APOP;
- break;
- }
+ char *lt;
+ char *gt = NULL;
+
+ /* Look for the APOP timestamp */
+ lt = memchr(line, '<', len);
+ if(lt)
+ /* search the remainder for '>' */
+ gt = memchr(lt, '>', len - (lt - line));
+ if(gt) {
+ /* the length of the timestamp, including the brackets */
+ size_t timestamplen = gt - lt + 1;
+ char *at = memchr(lt, '@', timestamplen);
+ /* If the timestamp does not contain '@' it is not (as required by
+ RFC-1939) conformant to the RFC-822 message id syntax, and we
+ therefore do not use APOP authentication. */
+ if(at) {
+ /* dupe the timestamp */
+ pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen);
+ if(!pop3c->apoptimestamp)
+ return CURLE_OUT_OF_MEMORY;
+ /* Store the APOP capability */
+ pop3c->authtypes |= POP3_TYPE_APOP;
}
}
- result = pop3_perform_capa(data, conn);
+ if(!result)
+ result = pop3_perform_capa(data, conn);
}
return result;
@@ -707,8 +699,8 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct pop3_conn *pop3c = &conn->proto.pop3c;
- const char *line = data->state.buffer;
- size_t len = strlen(line);
+ const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+ size_t len = data->conn->proto.pop3c.pp.nfinal;
(void)instate; /* no use for this yet */
@@ -795,7 +787,7 @@ static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
(void)instate; /* no use for this yet */
/* Pipelining in response is forbidden. */
- if(data->conn->proto.pop3c.pp.cache_size)
+ if(data->conn->proto.pop3c.pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(pop3code != '+') {
@@ -944,24 +936,29 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
/* POP3 download */
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
- if(pp->cache) {
- /* The header "cache" contains a bunch of data that is actually body
- content so send it as such. Note that there may even be additional
- "headers" after the body */
+ if(pp->overflow) {
+ /* The recv buffer contains data that is actually body content so send
+ it as such. Note that there may even be additional "headers" after
+ the body */
+
+ /* keep only the overflow */
+ Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+ pp->nfinal = 0; /* done */
if(!data->req.no_body) {
- result = Curl_pop3_write(data, pp->cache, pp->cache_size);
+ result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
+ Curl_dyn_len(&pp->recvbuf));
if(result)
return result;
}
- /* Free the cache */
- Curl_safefree(pp->cache);
-
- /* Reset the cache size */
- pp->cache_size = 0;
+ /* reset the buffer */
+ Curl_dyn_reset(&pp->recvbuf);
+ pp->overflow = 0;
}
}
+ else
+ pp->overflow = 0;
/* End of DO phase */
pop3_state(data, POP3_STOP);
@@ -1131,8 +1128,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
/* Initialise the pingpong layer */
- Curl_pp_setup(pp);
- Curl_pp_init(data, pp);
+ Curl_pp_init(pp);
/* Parse the URL options */
result = pop3_parse_url_options(conn);
diff --git a/libs/libcurl/src/progress.c b/libs/libcurl/src/progress.c
index a151cd2889..f15657155e 100644
--- a/libs/libcurl/src/progress.c
+++ b/libs/libcurl/src/progress.c
@@ -174,10 +174,18 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
data->progress.t_startop = timestamp;
break;
case TIMER_STARTSINGLE:
- /* This is set at the start of each single fetch */
+ /* This is set at the start of each single transfer */
data->progress.t_startsingle = timestamp;
data->progress.is_t_startransfer_set = false;
break;
+ case TIMER_POSTQUEUE:
+ /* Set when the transfer starts (after potentially having been brought
+ back from the waiting queue). It needs to count from t_startop and not
+ t_startsingle since the latter is reset when a connection is brought
+ back from the pending queue. */
+ data->progress.t_postqueue =
+ Curl_timediff_us(timestamp, data->progress.t_startop);
+ break;
case TIMER_STARTACCEPT:
data->progress.t_acceptdata = timestamp;
break;
diff --git a/libs/libcurl/src/progress.h b/libs/libcurl/src/progress.h
index 686e29c7f2..745dc1d5bb 100644
--- a/libs/libcurl/src/progress.h
+++ b/libs/libcurl/src/progress.h
@@ -30,7 +30,8 @@
typedef enum {
TIMER_NONE,
TIMER_STARTOP,
- TIMER_STARTSINGLE,
+ TIMER_STARTSINGLE, /* start of transfer, might get queued */
+ TIMER_POSTQUEUE, /* start, immediately after dequeue */
TIMER_NAMELOOKUP,
TIMER_CONNECT,
TIMER_APPCONNECT,
diff --git a/libs/libcurl/src/rand.c b/libs/libcurl/src/rand.c
index 025f8bdf4f..a0865e25b7 100644
--- a/libs/libcurl/src/rand.c
+++ b/libs/libcurl/src/rand.c
@@ -201,7 +201,7 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
{
CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
- DEBUGASSERT(num > 0);
+ DEBUGASSERT(num);
while(num) {
unsigned int r;
@@ -241,9 +241,11 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
memset(buffer, 0, sizeof(buffer));
#endif
- if((num/2 >= sizeof(buffer)) || !(num&1))
+ if((num/2 >= sizeof(buffer)) || !(num&1)) {
/* make sure it fits in the local buffer and that it is an odd number! */
+ DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex"));
return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
num--; /* save one for null-termination */
diff --git a/libs/libcurl/src/rtsp.c b/libs/libcurl/src/rtsp.c
index 3ad2efb690..17fff38511 100644
--- a/libs/libcurl/src/rtsp.c
+++ b/libs/libcurl/src/rtsp.c
@@ -58,21 +58,20 @@ static int rtsp_getsock_do(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
/*
- * Parse and write out any available RTP data.
+ * Parse and write out an RTSP response.
* @param data the transfer
* @param conn the connection
* @param buf data read from connection
* @param blen amount of data in buf
- * @param consumed out, number of blen consumed
+ * @param is_eos TRUE iff this is the last write
* @param readmore out, TRUE iff complete buf was consumed and more data
* is needed
*/
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
- struct connectdata *conn,
- const char *buf,
- size_t blen,
- size_t *pconsumed,
- bool *readmore);
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+ const char *buf,
+ size_t blen,
+ bool is_eos,
+ bool *done);
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
@@ -115,7 +114,7 @@ const struct Curl_handler Curl_handler_rtsp = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
rtsp_disconnect, /* disconnect */
- rtsp_rtp_readwrite, /* readwrite */
+ rtsp_rtp_write_resp, /* write_resp */
rtsp_conncheck, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_RTSP, /* defport */
@@ -590,26 +589,48 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
return result;
}
+/**
+ * write any BODY bytes missing to the client, ignore the rest.
+ */
+static CURLcode rtp_write_body_junk(struct Curl_easy *data,
+ const char *buf,
+ size_t blen)
+{
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+ curl_off_t body_remain;
+ bool in_body;
+
+ in_body = (data->req.headerline && !rtspc->in_header) &&
+ (data->req.size >= 0) &&
+ (data->req.bytecount < data->req.size);
+ body_remain = in_body? (data->req.size - data->req.bytecount) : 0;
+ DEBUGASSERT(body_remain >= 0);
+ if(body_remain) {
+ if((curl_off_t)blen > body_remain)
+ blen = (size_t)body_remain;
+ return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
+ }
+ return CURLE_OK;
+}
+
static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
- struct connectdata *conn,
const char *buf,
size_t blen,
- bool in_body,
size_t *pconsumed)
{
- struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
CURLcode result = CURLE_OK;
+ size_t skip_len = 0;
*pconsumed = 0;
while(blen) {
+ bool in_body = (data->req.headerline && !rtspc->in_header) &&
+ (data->req.size >= 0) &&
+ (data->req.bytecount < data->req.size);
switch(rtspc->state) {
case RTP_PARSE_SKIP: {
DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
- if(in_body && buf[0] != '$') {
- /* in BODY and no valid start, do not consume and return */
- goto out;
- }
while(blen && buf[0] != '$') {
if(!in_body && buf[0] == 'R' &&
data->set.rtspreq != RTSPREQ_RECEIVE) {
@@ -624,13 +645,22 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
goto out;
}
}
- /* junk, consume without buffering */
+ /* junk/BODY, consume without buffering */
*pconsumed += 1;
++buf;
--blen;
+ ++skip_len;
}
if(blen && buf[0] == '$') {
/* possible start of an RTP message, buffer */
+ if(skip_len) {
+ /* end of junk/BODY bytes, flush */
+ result = rtp_write_body_junk(data,
+ (char *)(buf - skip_len), skip_len);
+ skip_len = 0;
+ if(result)
+ goto out;
+ }
if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -650,35 +680,22 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
/* invalid channel number, junk or BODY data */
rtspc->state = RTP_PARSE_SKIP;
- if(in_body) {
- /* we do not consume this byte, it is BODY data */
- DEBUGF(infof(data, "RTSP: invalid RTP channel %d in BODY, "
- "treating as BODY data", idx));
- if(*pconsumed == 0) {
- /* We did not consume the initial '$' in our buffer, but had
- * it from an earlier call. We cannot un-consume it and have
- * to write it directly as BODY data */
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&rtspc->buf), 1);
- Curl_dyn_free(&rtspc->buf);
- if(result)
- goto out;
- }
- else {
- /* un-consume the '$' and leave */
- Curl_dyn_free(&rtspc->buf);
- *pconsumed -= 1;
- --buf;
- ++blen;
+ DEBUGASSERT(skip_len == 0);
+ /* we do not consume this byte, it is BODY data */
+ DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx));
+ if(*pconsumed == 0) {
+ /* We did not consume the initial '$' in our buffer, but had
+ * it from an earlier call. We cannot un-consume it and have
+ * to write it directly as BODY data */
+ result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
+ if(result)
goto out;
- }
}
else {
- /* not BODY, forget the junk '$'. Do not consume this byte,
- * it might be a start */
- infof(data, "RTSP: invalid RTP channel %d, skipping", idx);
- Curl_dyn_free(&rtspc->buf);
+ /* count the '$' as skip and continue */
+ skip_len = 1;
}
+ Curl_dyn_free(&rtspc->buf);
break;
}
/* a valid channel, so we expect this to be a real RTP message */
@@ -754,52 +771,51 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
}
}
out:
+ if(!result && skip_len)
+ result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
return result;
}
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
- struct connectdata *conn,
- const char *buf,
- size_t blen,
- size_t *pconsumed,
- bool *readmore)
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+ const char *buf,
+ size_t blen,
+ bool is_eos,
+ bool *done)
{
- struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+ struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
CURLcode result = CURLE_OK;
size_t consumed = 0;
- bool in_body;
if(!data->req.header)
rtspc->in_header = FALSE;
- in_body = (data->req.headerline && !rtspc->in_header) &&
- (data->req.size >= 0) &&
- (data->req.bytecount < data->req.size);
-
- *readmore = FALSE;
- *pconsumed = 0;
+ *done = FALSE;
if(!blen) {
goto out;
}
+ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
+ blen, rtspc->in_header, is_eos));
+
/* If header parsing is not onging, extract RTP messages */
if(!rtspc->in_header) {
- result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed);
+ result = rtsp_filter_rtp(data, buf, blen, &consumed);
if(result)
goto out;
- *pconsumed += consumed;
buf += consumed;
blen -= consumed;
+ /* either we consumed all or are at the start of header parsing */
+ if(blen && !data->req.header)
+ DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body",
+ blen));
}
/* we want to parse headers, do so */
if(data->req.header && blen) {
rtspc->in_header = TRUE;
- result = Curl_http_readwrite_headers(data, conn, buf, blen,
- &consumed);
+ result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
if(result)
goto out;
- *pconsumed += consumed;
buf += consumed;
blen -= consumed;
@@ -807,26 +823,41 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
rtspc->in_header = FALSE;
if(!rtspc->in_header) {
- /* If header parsing is done and data left, extract RTP messages */
- in_body = (data->req.headerline && !rtspc->in_header) &&
- (data->req.size >= 0) &&
- (data->req.bytecount < data->req.size);
- result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed);
+ /* If header parsing is done, extract interleaved RTP messages */
+ if(data->req.size <= -1) {
+ /* Respect section 4.4 of rfc2326: If the Content-Length header is
+ absent, a length 0 must be assumed. */
+ data->req.size = 0;
+ data->req.download_done = TRUE;
+ }
+ result = rtsp_filter_rtp(data, buf, blen, &consumed);
if(result)
goto out;
- *pconsumed += consumed;
+ blen -= consumed;
}
}
if(rtspc->state != RTP_PARSE_SKIP)
- *readmore = TRUE;
+ *done = FALSE;
+ /* we SHOULD have consumed all bytes, unless the response is borked.
+ * In which case we write out the left over bytes, letting the client
+ * writer deal with it (it will report EXCESS and fail the transfer). */
+ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
+ " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
+ blen, rtspc->in_header, *done, rtspc->state, data->req.size));
+ if(!result && (is_eos || blen)) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY|
+ (is_eos? CLIENTWRITE_EOS:0),
+ (char *)buf, blen);
+ }
out:
- if(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) {
+ if((data->set.rtspreq == RTSPREQ_RECEIVE) &&
+ (rtspc->state == RTP_PARSE_SKIP)) {
/* In special mode RECEIVE, we just process one chunk of network
* data, so we stop the transfer here, if we have no incomplete
* RTP message pending. */
- data->req.keepon &= ~KEEP_RECV;
+ data->req.download_done = TRUE;
}
return result;
}
@@ -922,7 +953,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
/* If the Session ID is set, then compare */
if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen ||
- strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen) != 0) {
+ strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) {
failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
start, data->set.str[STRING_RTSP_SESSION_ID]);
return CURLE_RTSP_SESSION_ERROR;
@@ -934,11 +965,9 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
*/
/* Copy the id substring into a new buffer */
- data->set.str[STRING_RTSP_SESSION_ID] = malloc(idlen + 1);
+ data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen);
if(!data->set.str[STRING_RTSP_SESSION_ID])
return CURLE_OUT_OF_MEMORY;
- memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, idlen);
- (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0';
}
}
else if(checkprefix("Transport:", header)) {
diff --git a/libs/libcurl/src/sendf.c b/libs/libcurl/src/sendf.c
index efd1723f17..7686e3c5ad 100644
--- a/libs/libcurl/src/sendf.c
+++ b/libs/libcurl/src/sendf.c
@@ -296,13 +296,6 @@ static CURLcode chop_write(struct Curl_easy *data,
if(!skip_body_write &&
((type & CLIENTWRITE_BODY) ||
((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
-#ifdef USE_WEBSOCKETS
- if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
- writebody = Curl_ws_writecb;
- writebody_ptr = data;
- }
- else
-#endif
writebody = data->set.fwrite_func;
}
if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
@@ -345,7 +338,7 @@ static CURLcode chop_write(struct Curl_easy *data,
len -= chunklen;
}
-#ifndef CURL_DISABLE_HTTP
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
/* HTTP header, but not status-line */
if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
@@ -404,10 +397,12 @@ CURLcode Curl_client_write(struct Curl_easy *data,
#endif
/* it is one of those, at least */
DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
- /* BODY is only BODY */
- DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY));
- /* INFO is only INFO */
- DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
+ /* BODY is only BODY (with optional EOS) */
+ DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
+ ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
+ /* INFO is only INFO (with optional EOS) */
+ DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
+ ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
if(!data->req.writer_stack) {
result = do_init_stack(data);
@@ -477,8 +472,6 @@ CURLcode Curl_cwriter_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
- if(!nbytes)
- return CURLE_OK;
if(!writer)
return CURLE_WRITE_ERROR;
return writer->cwt->do_write(data, writer, type, buf, nbytes);
@@ -556,7 +549,6 @@ static CURLcode cw_download_write(struct Curl_easy *data,
{
CURLcode result;
size_t nwrite, excess_len = 0;
- const char *excess_data = NULL;
if(!(type & CLIENTWRITE_BODY)) {
if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
@@ -564,13 +556,38 @@ static CURLcode cw_download_write(struct Curl_easy *data,
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
}
+ if(!data->req.bytecount) {
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+ if(data->req.exp100 > EXP100_SEND_DATA)
+ /* set time stamp to compare with when waiting for the 100 */
+ data->req.start100 = Curl_now();
+ }
+
+ /* Here, we deal with REAL BODY bytes. All filtering and transfer
+ * encodings have been applied and only the true content, e.g. BODY,
+ * bytes are passed here.
+ * This allows us to check sizes, update stats, etc. independent
+ * from the protocol in play. */
+
+ if(data->req.no_body && nbytes > 0) {
+ /* BODY arrives although we want none, bail out */
+ streamclose(data->conn, "ignoring body");
+ DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
+ nbytes));
+ data->req.download_done = TRUE;
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ /* Determine if we see any bytes in excess to what is allowed.
+ * We write the allowed bytes and handle excess further below.
+ * This gives deterministic BODY writes on varying buffer receive
+ * lengths. */
nwrite = nbytes;
if(-1 != data->req.maxdownload) {
size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
if(nwrite > wmax) {
excess_len = nbytes - wmax;
nwrite = wmax;
- excess_data = buf + nwrite;
}
if(nwrite == wmax) {
@@ -578,6 +595,8 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
}
+ /* Error on too large filesize is handled below, after writing
+ * the permitted bytes */
if(data->set.max_filesize) {
size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
if(nwrite > wmax) {
@@ -585,6 +604,7 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
}
+ /* Update stats, write and report progress */
data->req.bytecount += nwrite;
++data->req.bodywrites;
if(!data->req.ignorebody && nwrite) {
@@ -597,23 +617,7 @@ static CURLcode cw_download_write(struct Curl_easy *data,
return result;
if(excess_len) {
- if(data->conn->handler->readwrite) {
- /* RTSP hack moved from transfer loop to here */
- bool readmore = FALSE; /* indicates data is incomplete, need more */
- size_t consumed = 0;
- result = data->conn->handler->readwrite(data, data->conn,
- excess_data, excess_len,
- &consumed, &readmore);
- if(result)
- return result;
- DEBUGASSERT(consumed <= excess_len);
- excess_len -= consumed;
- if(readmore) {
- data->req.download_done = FALSE;
- data->req.keepon |= KEEP_RECV; /* we're not done reading */
- }
- }
- if(excess_len && !data->req.ignorebody) {
+ if(!data->req.ignorebody) {
infof(data,
"Excess found writing body:"
" excess = %zu"
@@ -762,6 +766,21 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
return CURLE_OK;
}
+void Curl_cwriter_remove_by_name(struct Curl_easy *data,
+ const char *name)
+{
+ struct Curl_cwriter **anchor = &data->req.writer_stack;
+
+ while(*anchor) {
+ if(!strcmp(name, (*anchor)->cwt->name)) {
+ struct Curl_cwriter *w = (*anchor);
+ *anchor = w->next;
+ Curl_cwriter_free(data, w);
+ continue;
+ }
+ anchor = &((*anchor)->next);
+ }
+}
/*
* Internal read-from-socket function. This is meant to deal with plain
diff --git a/libs/libcurl/src/sendf.h b/libs/libcurl/src/sendf.h
index a871b8d098..abe91eabe7 100644
--- a/libs/libcurl/src/sendf.h
+++ b/libs/libcurl/src/sendf.h
@@ -49,6 +49,7 @@
#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */
#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */
#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
+#define CLIENTWRITE_EOS (1<<7) /* End Of transfer download Stream */
/**
* Write `len` bytes at `prt` to the client. `type` indicates what
@@ -147,6 +148,9 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
CURLcode Curl_cwriter_add(struct Curl_easy *data,
struct Curl_cwriter *writer);
+void Curl_cwriter_remove_by_name(struct Curl_easy *data,
+ const char *name);
+
/**
* Convenience method for calling `writer->do_write()` that
* checks for NULL writer.
diff --git a/libs/libcurl/src/setopt.c b/libs/libcurl/src/setopt.c
index 5ad3c9456b..3bbca8be06 100644
--- a/libs/libcurl/src/setopt.c
+++ b/libs/libcurl/src/setopt.c
@@ -51,7 +51,7 @@
#include "altsvc.h"
#include "hsts.h"
#include "tftp.h"
-
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -366,6 +366,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
else
return CURLE_BAD_FUNCTION_ARGUMENT;
break;
+ case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS:
+ /*
+ * Option that specifies how quickly a server response must be obtained
+ * before it is considered failure. For pingpong protocols.
+ */
+ arg = va_arg(param, long);
+ if((arg >= 0) && (arg <= INT_MAX))
+ data->set.server_response_timeout = (unsigned int)arg;
+ else
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
#ifndef CURL_DISABLE_TFTP
case CURLOPT_TFTP_NO_OPTIONS:
/*
@@ -497,26 +508,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
(data->set.postfieldsize > (curl_off_t)((size_t)-1))))
result = CURLE_OUT_OF_MEMORY;
else {
- char *p;
-
- (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-
/* Allocate even when size == 0. This satisfies the need of possible
- later address compare to detect the COPYPOSTFIELDS mode, and
- to mark that postfields is used rather than read function or
- form data.
+ later address compare to detect the COPYPOSTFIELDS mode, and to
+ mark that postfields is used rather than read function or form
+ data.
*/
- p = malloc((size_t)(data->set.postfieldsize?
- data->set.postfieldsize:1));
-
+ char *p = Curl_memdup0(argptr, (size_t)data->set.postfieldsize);
+ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
if(!p)
result = CURLE_OUT_OF_MEMORY;
- else {
- if(data->set.postfieldsize)
- memcpy(p, argptr, (size_t)data->set.postfieldsize);
-
+ else
data->set.str[STRING_COPYPOSTFIELDS] = p;
- }
}
}
@@ -670,6 +672,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.opt_no_body = FALSE; /* this is implied */
Curl_mime_cleanpart(data->state.formp);
Curl_safefree(data->state.formp);
+ data->state.mimepost = NULL;
break;
#endif
@@ -977,6 +980,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
#ifndef CURL_DISABLE_FORM_API
Curl_mime_cleanpart(data->state.formp);
Curl_safefree(data->state.formp);
+ data->state.mimepost = NULL;
#endif
}
break;
@@ -3109,6 +3113,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_OUT_OF_MEMORY;
}
arg = va_arg(param, long);
+ if(!arg) {
+ DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input"));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
result = Curl_altsvc_ctrl(data->asi, arg);
if(result)
return result;
@@ -3163,5 +3171,9 @@ CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...)
result = Curl_vsetopt(data, tag, arg);
va_end(arg);
+#ifdef DEBUGBUILD
+ if(result == CURLE_BAD_FUNCTION_ARGUMENT)
+ infof(data, "setopt arg 0x%x returned CURLE_BAD_FUNCTION_ARGUMENT", tag);
+#endif
return result;
}
diff --git a/libs/libcurl/src/setup-win32.h b/libs/libcurl/src/setup-win32.h
index 2203c46a55..19f5cf154b 100644
--- a/libs/libcurl/src/setup-win32.h
+++ b/libs/libcurl/src/setup-win32.h
@@ -24,18 +24,53 @@
*
***************************************************************************/
+#undef USE_WINSOCK
+/* ---------------------------------------------------------------- */
+/* Watt-32 TCP/IP SPECIFIC */
+/* ---------------------------------------------------------------- */
+#ifdef USE_WATT32
+# include <tcp.h>
+# undef byte
+# undef word
+# define HAVE_SYS_IOCTL_H
+# define HAVE_SYS_SOCKET_H
+# define HAVE_NETINET_IN_H
+# define HAVE_NETDB_H
+# define HAVE_ARPA_INET_H
+# define SOCKET int
+/* ---------------------------------------------------------------- */
+/* BSD-style lwIP TCP/IP stack SPECIFIC */
+/* ---------------------------------------------------------------- */
+#elif defined(USE_LWIPSOCK)
+ /* Define to use BSD-style lwIP TCP/IP stack. */
+ /* #define USE_LWIPSOCK 1 */
+# undef HAVE_GETHOSTNAME
+# undef LWIP_POSIX_SOCKETS_IO_NAMES
+# undef RECV_TYPE_ARG1
+# undef RECV_TYPE_ARG3
+# undef SEND_TYPE_ARG1
+# undef SEND_TYPE_ARG3
+# define HAVE_GETHOSTBYNAME_R
+# define HAVE_GETHOSTBYNAME_R_6
+# define LWIP_POSIX_SOCKETS_IO_NAMES 0
+# define RECV_TYPE_ARG1 int
+# define RECV_TYPE_ARG3 size_t
+# define SEND_TYPE_ARG1 int
+# define SEND_TYPE_ARG3 size_t
+#elif defined(_WIN32)
+# define USE_WINSOCK 2
+#endif
+
/*
* Include header files for windows builds before redefining anything.
* Use this preprocessor block only to include or exclude windows.h,
* winsock2.h or ws2tcpip.h. Any other windows thing belongs
* to any other further and independent block. Under Cygwin things work
* just as under linux (e.g. <sys/socket.h>) and the winsock headers should
- * never be included when __CYGWIN__ is defined. configure script takes
- * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H,
- * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined.
+ * never be included when __CYGWIN__ is defined.
*/
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
# if defined(UNICODE) && !defined(_UNICODE)
# error "UNICODE is defined but _UNICODE is not defined"
# endif
@@ -53,12 +88,8 @@
# ifndef NOGDI
# define NOGDI
# endif
-# ifdef HAVE_WINSOCK2_H
-# include <winsock2.h>
-# ifdef HAVE_WS2TCPIP_H
-# include <ws2tcpip.h>
-# endif
-# endif
+# include <winsock2.h>
+# include <ws2tcpip.h>
# include <windows.h>
# include <winerror.h>
# include <tchar.h>
@@ -68,17 +99,6 @@
#endif
/*
- * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else
- * undefine USE_WINSOCK.
- */
-
-#undef USE_WINSOCK
-
-#ifdef HAVE_WINSOCK2_H
-# define USE_WINSOCK 2
-#endif
-
-/*
* Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have
* those symbols to compare against, and even those that do may be missing
* newer symbols.
diff --git a/libs/libcurl/src/share.c b/libs/libcurl/src/share.c
index 5898c713d3..96accead9a 100644
--- a/libs/libcurl/src/share.c
+++ b/libs/libcurl/src/share.c
@@ -133,13 +133,13 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
res = CURLSHE_BAD_OPTION;
}
if(!res)
- share->specifier |= (1<<type);
+ share->specifier |= (unsigned int)(1<<type);
break;
case CURLSHOPT_UNSHARE:
/* this is a type this share will no longer share */
type = va_arg(param, int);
- share->specifier &= ~(1<<type);
+ share->specifier &= ~(unsigned int)(1<<type);
switch(type) {
case CURL_LOCK_DATA_DNS:
break;
@@ -264,7 +264,7 @@ Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
if(!share)
return CURLSHE_INVALID;
- if(share->specifier & (1<<type)) {
+ if(share->specifier & (unsigned int)(1<<type)) {
if(share->lockfunc) /* only call this if set! */
share->lockfunc(data, type, accesstype, share->clientdata);
}
@@ -281,7 +281,7 @@ Curl_share_unlock(struct Curl_easy *data, curl_lock_data type)
if(!share)
return CURLSHE_INVALID;
- if(share->specifier & (1<<type)) {
+ if(share->specifier & (unsigned int)(1<<type)) {
if(share->unlockfunc) /* only call this if set! */
share->unlockfunc (data, type, share->clientdata);
}
diff --git a/libs/libcurl/src/smb.c b/libs/libcurl/src/smb.c
index 130382c740..b9a9f5702c 100644
--- a/libs/libcurl/src/smb.c
+++ b/libs/libcurl/src/smb.c
@@ -272,7 +272,7 @@ const struct Curl_handler Curl_handler_smb = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
smb_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMB, /* defport */
@@ -299,7 +299,7 @@ const struct Curl_handler Curl_handler_smbs = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
smb_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMBS, /* defport */
diff --git a/libs/libcurl/src/smtp.c b/libs/libcurl/src/smtp.c
index b6da9ba679..aac4e83c67 100644
--- a/libs/libcurl/src/smtp.c
+++ b/libs/libcurl/src/smtp.c
@@ -130,7 +130,7 @@ const struct Curl_handler Curl_handler_smtp = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
smtp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMTP, /* defport */
@@ -159,7 +159,7 @@ const struct Curl_handler Curl_handler_smtps = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
smtp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SMTPS, /* defport */
@@ -250,8 +250,8 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = data->state.buffer;
- size_t len = strlen(message);
+ char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
+ size_t len = data->conn->proto.smtpc.pp.nfinal;
if(len > 4) {
/* Find the start of the message */
@@ -859,7 +859,7 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
(void)instate; /* no use for this yet */
/* Pipelining in response is forbidden. */
- if(data->conn->proto.smtpc.pp.cache_size)
+ if(data->conn->proto.smtpc.pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(smtpcode != 220) {
@@ -883,8 +883,8 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
struct smtp_conn *smtpc = &conn->proto.smtpc;
- const char *line = data->state.buffer;
- size_t len = strlen(line);
+ const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
+ size_t len = smtpc->pp.nfinal;
(void)instate; /* no use for this yet */
@@ -1033,8 +1033,8 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
{
CURLcode result = CURLE_OK;
struct SMTP *smtp = data->req.p.smtp;
- char *line = data->state.buffer;
- size_t len = strlen(line);
+ char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
+ size_t len = data->conn->proto.smtpc.pp.nfinal;
(void)instate; /* no use for this yet */
@@ -1044,12 +1044,8 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
result = CURLE_WEIRD_SERVER_REPLY;
}
else {
- /* Temporarily add the LF character back and send as body to the client */
- if(!data->req.no_body) {
- line[len] = '\n';
- result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
- line[len] = '\0';
- }
+ if(!data->req.no_body)
+ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
if(smtpcode != 1) {
if(smtp->rcpt) {
@@ -1268,7 +1264,6 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
break;
case SMTP_QUIT:
- /* fallthrough, just stop! */
default:
/* internal error */
smtp_state(data, SMTP_STOP);
@@ -1362,8 +1357,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
/* Initialise the pingpong layer */
- Curl_pp_setup(pp);
- Curl_pp_init(data, pp);
+ Curl_pp_init(pp);
/* Parse the URL options */
result = smtp_parse_url_options(conn);
@@ -1541,6 +1535,8 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
static CURLcode smtp_do(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
*done = FALSE; /* default to false */
/* Parse the custom request */
diff --git a/libs/libcurl/src/socketpair.c b/libs/libcurl/src/socketpair.c
index 9d4020426f..90b51d9017 100644
--- a/libs/libcurl/src/socketpair.c
+++ b/libs/libcurl/src/socketpair.c
@@ -33,9 +33,6 @@
* This is a socketpair() implementation for Windows.
*/
#include <string.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <windows.h>
#include <io.h>
#else
#ifdef HAVE_NETDB_H
diff --git a/libs/libcurl/src/socks.c b/libs/libcurl/src/socks.c
index 2a8f106510..ea733d9e98 100644
--- a/libs/libcurl/src/socks.c
+++ b/libs/libcurl/src/socks.c
@@ -71,9 +71,18 @@ enum connect_t {
CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
};
+#define CURL_SOCKS_BUF_SIZE 600
+
+/* make sure we configure it not too low */
+#if CURL_SOCKS_BUF_SIZE < 600
+#error CURL_SOCKS_BUF_SIZE must be at least 600
+#endif
+
+
struct socks_state {
enum connect_t state;
ssize_t outstanding; /* send this many bytes more */
+ unsigned char buffer[CURL_SOCKS_BUF_SIZE];
unsigned char *outp; /* send from this pointer */
const char *hostname;
@@ -249,7 +258,7 @@ static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
failf(data, "connection to proxy closed");
return CURLPX_CLOSED;
}
- failf(data, "SOCKS4: Failed receiving %s: %s", description,
+ failf(data, "SOCKS: Failed receiving %s: %s", description,
curl_easy_strerror(result));
return failcode;
}
@@ -278,14 +287,11 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
struct connectdata *conn = cf->conn;
const bool protocol4a =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
- unsigned char *socksreq = (unsigned char *)data->state.buffer;
+ unsigned char *socksreq = sx->buffer;
CURLcode result;
CURLproxycode presult;
struct Curl_dns_entry *dns = NULL;
- /* make sure that the buffer is at least 600 bytes */
- DEBUGASSERT(READBUFFER_MIN >= 600);
-
switch(sx->state) {
case CONNECT_SOCKS_INIT:
/* SOCKS4 can only do IPv4, insist! */
@@ -339,8 +345,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
if(dns) {
#ifdef CURLRES_ASYNCH
- conn->resolve_async.dns = dns;
- conn->resolve_async.done = TRUE;
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
#endif
infof(data, "Hostname '%s' was found", sx->hostname);
sxstate(sx, data, CONNECT_RESOLVED);
@@ -353,9 +359,10 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
return CURLPX_OK;
}
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
+ case CONNECT_RESOLVED:
CONNECT_RESOLVED:
- case CONNECT_RESOLVED: {
+ {
struct Curl_addrinfo *hp = NULL;
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
@@ -393,9 +400,9 @@ CONNECT_RESOLVED:
if(!hp)
return CURLPX_RESOLVE_HOST;
}
- /* FALLTHROUGH */
-CONNECT_REQ_INIT:
+ FALLTHROUGH();
case CONNECT_REQ_INIT:
+CONNECT_REQ_INIT:
/*
* This is currently not supporting "Identification Protocol (RFC1413)".
*/
@@ -430,7 +437,7 @@ CONNECT_REQ_INIT:
/* append hostname */
hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
if((hostnamelen <= 255) &&
- (packetsize + hostnamelen < data->set.buffer_size))
+ (packetsize + hostnamelen < sizeof(sx->buffer)))
strcpy((char *)socksreq + packetsize, sx->hostname);
else {
failf(data, "SOCKS4: too long host name");
@@ -439,10 +446,11 @@ CONNECT_REQ_INIT:
packetsize += hostnamelen;
}
sx->outp = socksreq;
+ DEBUGASSERT(packetsize <= sizeof(sx->buffer));
sx->outstanding = packetsize;
sxstate(sx, data, CONNECT_REQ_SENDING);
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_REQ_SENDING:
/* Send request */
presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
@@ -458,7 +466,7 @@ CONNECT_REQ_INIT:
sx->outp = socksreq;
sxstate(sx, data, CONNECT_SOCKS_READ);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_SOCKS_READ:
/* Receive response */
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
@@ -570,14 +578,14 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
o X'00' succeeded
*/
struct connectdata *conn = cf->conn;
- unsigned char *socksreq = (unsigned char *)data->state.buffer;
- int idx;
+ unsigned char *socksreq = sx->buffer;
+ size_t idx;
CURLcode result;
CURLproxycode presult;
bool socks5_resolve_local =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
const size_t hostname_len = strlen(sx->hostname);
- ssize_t len = 0;
+ size_t len = 0;
const unsigned char auth = data->set.socks5auth;
bool allow_gssapi = FALSE;
struct Curl_dns_entry *dns = NULL;
@@ -620,6 +628,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
socksreq[1] = (unsigned char) (idx - 2);
sx->outp = socksreq;
+ DEBUGASSERT(idx <= sizeof(sx->buffer));
sx->outstanding = idx;
presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
"initial SOCKS5 request");
@@ -640,12 +649,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
/* remain in sending state */
return CURLPX_OK;
}
- /* FALLTHROUGH */
-CONNECT_SOCKS_READ_INIT:
+ FALLTHROUGH();
case CONNECT_SOCKS_READ_INIT:
+CONNECT_SOCKS_READ_INIT:
sx->outstanding = 2; /* expect two bytes */
sx->outp = socksreq; /* store it here */
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_SOCKS_READ:
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
"initial SOCKS5 response");
@@ -746,10 +755,11 @@ CONNECT_AUTH_INIT:
}
len += proxy_password_len;
sxstate(sx, data, CONNECT_AUTH_SEND);
+ DEBUGASSERT(len <= sizeof(sx->buffer));
sx->outstanding = len;
sx->outp = socksreq;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_AUTH_SEND:
presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
"SOCKS5 sub-negotiation request");
@@ -762,7 +772,7 @@ CONNECT_AUTH_INIT:
sx->outp = socksreq;
sx->outstanding = 2;
sxstate(sx, data, CONNECT_AUTH_READ);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_AUTH_READ:
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
"SOCKS5 sub-negotiation response");
@@ -781,9 +791,9 @@ CONNECT_AUTH_INIT:
/* Everything is good so far, user was authenticated! */
sxstate(sx, data, CONNECT_REQ_INIT);
- /* FALLTHROUGH */
-CONNECT_REQ_INIT:
+ FALLTHROUGH();
case CONNECT_REQ_INIT:
+CONNECT_REQ_INIT:
if(socks5_resolve_local) {
enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
TRUE, &dns);
@@ -806,8 +816,8 @@ CONNECT_REQ_INIT:
if(dns) {
#ifdef CURLRES_ASYNCH
- conn->resolve_async.dns = dns;
- conn->resolve_async.done = TRUE;
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
#endif
infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
}
@@ -820,9 +830,10 @@ CONNECT_REQ_INIT:
return CURLPX_OK;
}
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
+ case CONNECT_RESOLVED:
CONNECT_RESOLVED:
- case CONNECT_RESOLVED: {
+ {
char dest[MAX_IPADR_LEN]; /* printable address */
struct Curl_addrinfo *hp = NULL;
if(dns)
@@ -923,10 +934,10 @@ CONNECT_RESOLVE_REMOTE:
infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
sx->hostname, sx->remote_port);
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
-CONNECT_REQ_SEND:
case CONNECT_REQ_SEND:
+CONNECT_REQ_SEND:
/* PORT MSB */
socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
/* PORT LSB */
@@ -939,9 +950,10 @@ CONNECT_REQ_SEND:
}
#endif
sx->outp = socksreq;
+ DEBUGASSERT(len <= sizeof(sx->buffer));
sx->outstanding = len;
sxstate(sx, data, CONNECT_REQ_SENDING);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_REQ_SENDING:
presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
"SOCKS5 connect request");
@@ -960,7 +972,7 @@ CONNECT_REQ_SEND:
sx->outstanding = 10; /* minimum packet size is 10 */
sx->outp = socksreq;
sxstate(sx, data, CONNECT_REQ_READ);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_REQ_READ:
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
"SOCKS5 connect request ack");
@@ -1038,6 +1050,7 @@ CONNECT_REQ_SEND:
/* decrypt_gssapi_blockread already read the whole packet */
#endif
if(len > 10) {
+ DEBUGASSERT(len <= sizeof(sx->buffer));
sx->outstanding = len - 10; /* get the rest */
sx->outp = &socksreq[10];
sxstate(sx, data, CONNECT_REQ_READ_MORE);
@@ -1049,7 +1062,7 @@ CONNECT_REQ_SEND:
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
}
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CONNECT_REQ_READ_MORE:
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
"SOCKS5 connect request address");
diff --git a/libs/libcurl/src/socks_gssapi.c b/libs/libcurl/src/socks_gssapi.c
index 7a4bcf258e..d9dda91aa0 100644
--- a/libs/libcurl/src/socks_gssapi.c
+++ b/libs/libcurl/src/socks_gssapi.c
@@ -35,6 +35,7 @@
#include "timeval.h"
#include "socks.h"
#include "warnless.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -139,10 +140,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
/* prepare service name */
if(strchr(serviceptr, '/')) {
service.length = serviceptr_length;
- service.value = malloc(service.length);
+ service.value = Curl_memdup(serviceptr, service.length);
if(!service.value)
return CURLE_OUT_OF_MEMORY;
- memcpy(service.value, serviceptr, service.length);
gss_major_status = gss_import_name(&gss_minor_status, &service,
(gss_OID) GSS_C_NULL_OID, &server);
@@ -387,12 +387,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
}
else {
gss_send_token.length = 1;
- gss_send_token.value = malloc(1);
+ gss_send_token.value = Curl_memdup(&gss_enc, 1);
if(!gss_send_token.value) {
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_OUT_OF_MEMORY;
}
- memcpy(gss_send_token.value, &gss_enc, 1);
gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
GSS_C_QOP_DEFAULT, &gss_send_token,
diff --git a/libs/libcurl/src/socks_sspi.c b/libs/libcurl/src/socks_sspi.c
index c5010a9197..dc14e7e3e7 100644
--- a/libs/libcurl/src/socks_sspi.c
+++ b/libs/libcurl/src/socks_sspi.c
@@ -331,9 +331,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
failf(data, "Failed to determine user name.");
return CURLE_COULDNT_CONNECT;
}
- infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
- names.sUserName);
- s_pSecFn->FreeContextBuffer(names.sUserName);
+ else {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName);
+ infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
+ (user_utf8 ? user_utf8 : "(unknown)"));
+ curlx_unicodefree(user_utf8);
+#endif
+ s_pSecFn->FreeContextBuffer(names.sUserName);
+ }
/* Do encryption */
socksreq[0] = 1; /* GSS-API subnegotiation version */
diff --git a/libs/libcurl/src/strdup.c b/libs/libcurl/src/strdup.c
index ee341ef27e..f3d8ed3846 100644
--- a/libs/libcurl/src/strdup.c
+++ b/libs/libcurl/src/strdup.c
@@ -101,21 +101,17 @@ void *Curl_memdup(const void *src, size_t length)
/***************************************************************************
*
- * Curl_strndup(source, length)
+ * Curl_memdup0(source, length)
*
* Copies the 'source' string to a newly allocated buffer (that is returned).
- * Copies not more than 'length' bytes (up to a null terminator) then adds a
- * null terminator.
+ * Copies 'length' bytes then adds a null terminator.
*
* Returns the new pointer or NULL on failure.
*
***************************************************************************/
-void *Curl_strndup(const char *src, size_t length)
+void *Curl_memdup0(const char *src, size_t length)
{
- char *buf = memchr(src, '\0', length);
- if(buf)
- length = buf - src;
- buf = malloc(length + 1);
+ char *buf = malloc(length + 1);
if(!buf)
return NULL;
memcpy(buf, src, length);
diff --git a/libs/libcurl/src/strdup.h b/libs/libcurl/src/strdup.h
index 7c7862db45..763952cc12 100644
--- a/libs/libcurl/src/strdup.h
+++ b/libs/libcurl/src/strdup.h
@@ -33,6 +33,6 @@ wchar_t* Curl_wcsdup(const wchar_t* src);
#endif
void *Curl_memdup(const void *src, size_t buffer_length);
void *Curl_saferealloc(void *ptr, size_t size);
-void *Curl_strndup(const char *src, size_t length);
+void *Curl_memdup0(const char *src, size_t length);
#endif /* HEADER_CURL_STRDUP_H */
diff --git a/libs/libcurl/src/strerror.c b/libs/libcurl/src/strerror.c
index 57105759d2..8d4abe4198 100644
--- a/libs/libcurl/src/strerror.c
+++ b/libs/libcurl/src/strerror.c
@@ -319,6 +319,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_UNRECOVERABLE_POLL:
return "Unrecoverable error in select/poll";
+ case CURLE_TOO_LARGE:
+ return "A value or data field grew larger than allowed";
+
/* error codes not used by current libcurl */
case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24:
@@ -553,6 +556,9 @@ curl_url_strerror(CURLUcode error)
case CURLUE_LACKS_IDN:
return "libcurl lacks IDN support";
+ case CURLUE_TOO_LARGE:
+ return "A value or data field is larger than allowed";
+
case CURLUE_LAST:
break;
}
@@ -572,10 +578,11 @@ curl_url_strerror(CURLUcode error)
* Returns NULL if no error message was found for error code.
*/
static const char *
-get_winsock_error (int err, char *buf, size_t len)
+get_winsock_error(int err, char *buf, size_t len)
{
#ifndef CURL_DISABLE_VERBOSE_STRINGS
const char *p;
+ size_t alen;
#endif
if(!len)
@@ -755,8 +762,9 @@ get_winsock_error (int err, char *buf, size_t len)
default:
return NULL;
}
- strncpy(buf, p, len);
- buf [len-1] = '\0';
+ alen = strlen(p);
+ if(alen < len)
+ strcpy(buf, p);
return buf;
#endif
}
@@ -832,7 +840,6 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
#endif
int old_errno = errno;
char *p;
- size_t max;
if(!buflen)
return NULL;
@@ -841,23 +848,22 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
DEBUGASSERT(err >= 0);
#endif
- max = buflen - 1;
*buf = '\0';
#if defined(_WIN32) || defined(_WIN32_WCE)
#if defined(_WIN32)
/* 'sys_nerr' is the maximum errno number, it is not widely portable */
if(err >= 0 && err < sys_nerr)
- strncpy(buf, sys_errlist[err], max);
+ msnprintf(buf, buflen, "%s", sys_errlist[err]);
else
#endif
{
if(
#ifdef USE_WINSOCK
- !get_winsock_error(err, buf, max) &&
+ !get_winsock_error(err, buf, buflen) &&
#endif
- !get_winapi_error((DWORD)err, buf, max))
- msnprintf(buf, max, "Unknown error %d (%#x)", err, err);
+ !get_winapi_error((DWORD)err, buf, buflen))
+ msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
}
#else /* not Windows coming up */
@@ -867,9 +873,9 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
* storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
* message string, or EINVAL if 'errnum' is not a valid error number.
*/
- if(0 != strerror_r(err, buf, max)) {
+ if(0 != strerror_r(err, buf, buflen)) {
if('\0' == buf[0])
- msnprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, buflen, "Unknown error %d", err);
}
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
/*
@@ -881,25 +887,23 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
char buffer[256];
char *msg = strerror_r(err, buffer, sizeof(buffer));
if(msg)
- strncpy(buf, msg, max);
+ msnprintf(buf, buflen, "%s", msg);
else
- msnprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, buflen, "Unknown error %d", err);
}
#else
{
/* !checksrc! disable STRERROR 1 */
const char *msg = strerror(err);
if(msg)
- strncpy(buf, msg, max);
+ msnprintf(buf, buflen, "%s", msg);
else
- msnprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, buflen, "Unknown error %d", err);
}
#endif
#endif /* end of not Windows */
- buf[max] = '\0'; /* make sure the string is null-terminated */
-
/* strip trailing '\r\n' or '\n'. */
p = strrchr(buf, '\n');
if(p && (p - buf) >= 2)
@@ -943,8 +947,8 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
#else
{
const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
- strncpy(buf, txt, buflen);
- buf[buflen - 1] = '\0';
+ if(strlen(txt) < buflen)
+ strcpy(buf, txt);
}
#endif
@@ -1081,17 +1085,11 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
err);
}
else {
- char txtbuf[80];
char msgbuf[256];
-
- msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
-
if(get_winapi_error(err, msgbuf, sizeof(msgbuf)))
- msnprintf(buf, buflen, "%s - %s", txtbuf, msgbuf);
- else {
- strncpy(buf, txtbuf, buflen);
- buf[buflen - 1] = '\0';
- }
+ msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
+ else
+ msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
}
#else
@@ -1099,8 +1097,8 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
txt = "No error";
else
txt = "Error";
- strncpy(buf, txt, buflen);
- buf[buflen - 1] = '\0';
+ if(buflen > strlen(txt))
+ strcpy(buf, txt);
#endif
if(errno != old_errno)
diff --git a/libs/libcurl/src/system_win32.c b/libs/libcurl/src/system_win32.c
index 10d7fa9c27..fc20e09f31 100644
--- a/libs/libcurl/src/system_win32.c
+++ b/libs/libcurl/src/system_win32.c
@@ -38,16 +38,23 @@
LARGE_INTEGER Curl_freq;
bool Curl_isVistaOrGreater;
+bool Curl_isWindows8OrGreater;
/* Handle of iphlpapp.dll */
static HMODULE s_hIpHlpApiDll = NULL;
-/* Pointer to the if_nametoindex function */
+/* Function pointers */
IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL;
+FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW = NULL;
+GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel = NULL;
+GETADDRINFOEXW_FN Curl_GetAddrInfoExW = NULL;
/* Curl_win32_init() performs win32 global initialization */
CURLcode Curl_win32_init(long flags)
{
+#ifdef USE_WINSOCK
+ HMODULE ws2_32Dll;
+#endif
/* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which
is just for Winsock at the moment. Any required win32 initialization
should take place after this block. */
@@ -104,6 +111,18 @@ CURLcode Curl_win32_init(long flags)
Curl_if_nametoindex = pIfNameToIndex;
}
+#ifdef USE_WINSOCK
+ ws2_32Dll = GetModuleHandleA("ws2_32");
+ if(ws2_32Dll) {
+ Curl_FreeAddrInfoExW = CURLX_FUNCTION_CAST(FREEADDRINFOEXW_FN,
+ GetProcAddress(ws2_32Dll, "FreeAddrInfoExW"));
+ Curl_GetAddrInfoExCancel = CURLX_FUNCTION_CAST(GETADDRINFOEXCANCEL_FN,
+ GetProcAddress(ws2_32Dll, "GetAddrInfoExCancel"));
+ Curl_GetAddrInfoExW = CURLX_FUNCTION_CAST(GETADDRINFOEXW_FN,
+ GetProcAddress(ws2_32Dll, "GetAddrInfoExW"));
+ }
+#endif
+
/* curlx_verify_windows_version must be called during init at least once
because it has its own initialization routine. */
if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
@@ -113,6 +132,13 @@ CURLcode Curl_win32_init(long flags)
else
Curl_isVistaOrGreater = FALSE;
+ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ Curl_isWindows8OrGreater = TRUE;
+ }
+ else
+ Curl_isWindows8OrGreater = FALSE;
+
QueryPerformanceFrequency(&Curl_freq);
return CURLE_OK;
}
@@ -120,6 +146,9 @@ CURLcode Curl_win32_init(long flags)
/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */
void Curl_win32_cleanup(long init_flags)
{
+ Curl_FreeAddrInfoExW = NULL;
+ Curl_GetAddrInfoExCancel = NULL;
+ Curl_GetAddrInfoExW = NULL;
if(s_hIpHlpApiDll) {
FreeLibrary(s_hIpHlpApiDll);
s_hIpHlpApiDll = NULL;
diff --git a/libs/libcurl/src/system_win32.h b/libs/libcurl/src/system_win32.h
index f14ae8e4e5..aaae969f74 100644
--- a/libs/libcurl/src/system_win32.h
+++ b/libs/libcurl/src/system_win32.h
@@ -26,10 +26,11 @@
#include "curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
extern LARGE_INTEGER Curl_freq;
extern bool Curl_isVistaOrGreater;
+extern bool Curl_isWindows8OrGreater;
CURLcode Curl_win32_init(long flags);
void Curl_win32_cleanup(long init_flags);
@@ -40,6 +41,33 @@ typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *);
/* This is used instead of if_nametoindex if available on Windows */
extern IF_NAMETOINDEX_FN Curl_if_nametoindex;
+/* Identical copy of addrinfoexW/ADDRINFOEXW */
+typedef struct addrinfoexW_
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ PWSTR ai_canonname;
+ struct sockaddr *ai_addr;
+ void *ai_blob;
+ size_t ai_bloblen;
+ LPGUID ai_provider;
+ struct addrinfoexW_ *ai_next;
+} ADDRINFOEXW_;
+
+typedef void (CALLBACK *LOOKUP_COMPLETION_FN)(DWORD, DWORD, LPWSAOVERLAPPED);
+typedef void (WSAAPI *FREEADDRINFOEXW_FN)(ADDRINFOEXW_*);
+typedef int (WSAAPI *GETADDRINFOEXCANCEL_FN)(LPHANDLE);
+typedef int (WSAAPI *GETADDRINFOEXW_FN)(PCWSTR, PCWSTR, DWORD, LPGUID,
+ const ADDRINFOEXW_*, ADDRINFOEXW_**, struct timeval*, LPOVERLAPPED,
+ LOOKUP_COMPLETION_FN, LPHANDLE);
+
+extern FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW;
+extern GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel;
+extern GETADDRINFOEXW_FN Curl_GetAddrInfoExW;
+
/* This is used to dynamically load DLLs */
HMODULE Curl_load_library(LPCTSTR filename);
#else /* _WIN32 */
diff --git a/libs/libcurl/src/telnet.c b/libs/libcurl/src/telnet.c
index 9f2cc0f2fb..f80326b913 100644
--- a/libs/libcurl/src/telnet.c
+++ b/libs/libcurl/src/telnet.c
@@ -160,6 +160,7 @@ struct TELNET {
unsigned short subopt_wsy; /* Set with suboption NAWS */
TelnetReceive telrcv_state;
struct curl_slist *telnet_vars; /* Environment variables */
+ struct dynbuf out; /* output buffer */
/* suboptions */
unsigned char subbuffer[SUBBUFSIZE];
@@ -185,7 +186,7 @@ const struct Curl_handler Curl_handler_telnet = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_TELNET, /* defport */
@@ -204,6 +205,7 @@ CURLcode init_telnet(struct Curl_easy *data)
if(!tn)
return CURLE_OUT_OF_MEMORY;
+ Curl_dyn_init(&tn->out, 0xffff);
data->req.p.telnet = tn; /* make us known */
tn->telrcv_state = CURL_TS_DATA;
@@ -799,8 +801,10 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
was given on the command line */
if(data->state.aptr.user) {
char buffer[256];
- if(str_is_nonascii(data->conn->user))
+ if(str_is_nonascii(data->conn->user)) {
+ DEBUGF(infof(data, "set a non ASCII user name in telnet"));
return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
beg = curl_slist_append(tn->telnet_vars, buffer);
if(!beg) {
@@ -826,23 +830,27 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
case 5:
/* Terminal type */
if(strncasecompare(option, "TTYPE", 5)) {
- strncpy(tn->subopt_ttype, arg, 31);
- tn->subopt_ttype[31] = 0; /* String termination */
- tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ size_t l = strlen(arg);
+ if(l < sizeof(tn->subopt_ttype)) {
+ strcpy(tn->subopt_ttype, arg);
+ tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ break;
+ }
}
- else
- result = CURLE_UNKNOWN_OPTION;
+ result = CURLE_UNKNOWN_OPTION;
break;
case 8:
/* Display variable */
if(strncasecompare(option, "XDISPLOC", 8)) {
- strncpy(tn->subopt_xdisploc, arg, 127);
- tn->subopt_xdisploc[127] = 0; /* String termination */
- tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+ size_t l = strlen(arg);
+ if(l < sizeof(tn->subopt_xdisploc)) {
+ strcpy(tn->subopt_xdisploc, arg);
+ tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+ break;
+ }
}
- else
- result = CURLE_UNKNOWN_OPTION;
+ result = CURLE_UNKNOWN_OPTION;
break;
case 7:
@@ -1223,37 +1231,33 @@ process_iac:
static CURLcode send_telnet_data(struct Curl_easy *data,
char *buffer, ssize_t nread)
{
- ssize_t escapes, i, outlen;
- unsigned char *outbuf = NULL;
+ ssize_t i, outlen;
+ unsigned char *outbuf;
CURLcode result = CURLE_OK;
- ssize_t bytes_written, total_written;
+ ssize_t bytes_written, total_written = 0;
struct connectdata *conn = data->conn;
+ struct TELNET *tn = data->req.p.telnet;
- /* Determine size of new buffer after escaping */
- escapes = 0;
- for(i = 0; i < nread; i++)
- if((unsigned char)buffer[i] == CURL_IAC)
- escapes++;
- outlen = nread + escapes;
+ DEBUGASSERT(tn);
- if(outlen == nread)
- outbuf = (unsigned char *)buffer;
- else {
- ssize_t j;
- outbuf = malloc(nread + escapes + 1);
- if(!outbuf)
- return CURLE_OUT_OF_MEMORY;
+ if(memchr(buffer, CURL_IAC, nread)) {
+ /* only use the escape buffer when necessary */
+ Curl_dyn_reset(&tn->out);
- j = 0;
- for(i = 0; i < nread; i++) {
- outbuf[j++] = (unsigned char)buffer[i];
- if((unsigned char)buffer[i] == CURL_IAC)
- outbuf[j++] = CURL_IAC;
+ for(i = 0; i < nread && !result; i++) {
+ result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
+ if(!result && ((unsigned char)buffer[i] == CURL_IAC))
+ /* IAC is FF in hex */
+ result = Curl_dyn_addn(&tn->out, "\xff", 1);
}
- outbuf[j] = '\0';
- }
- total_written = 0;
+ outlen = Curl_dyn_len(&tn->out);
+ outbuf = Curl_dyn_uptr(&tn->out);
+ }
+ else {
+ outlen = nread;
+ outbuf = (unsigned char *)buffer;
+ }
while(!result && total_written < outlen) {
/* Make sure socket is writable to avoid EWOULDBLOCK condition */
struct pollfd pfd[1];
@@ -1266,19 +1270,13 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
break;
default: /* write! */
bytes_written = 0;
- result = Curl_nwrite(data, 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;
}
}
- /* Free malloc copy if escaped */
- if(outbuf != (unsigned char *)buffer)
- free(outbuf);
-
return result;
}
@@ -1294,6 +1292,7 @@ static CURLcode telnet_done(struct Curl_easy *data,
curl_slist_free_all(tn->telnet_vars);
tn->telnet_vars = NULL;
+ Curl_dyn_free(&tn->out);
return CURLE_OK;
}
@@ -1321,7 +1320,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
ssize_t nread;
struct curltime now;
bool keepon = TRUE;
- char *buf = data->state.buffer;
+ char buffer[4*1024];
struct TELNET *tn;
*done = TRUE; /* unconditionally */
@@ -1378,7 +1377,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
/* Keep on listening and act on events */
while(keepon) {
- const DWORD buf_size = (DWORD)data->set.buffer_size;
+ const DWORD buf_size = (DWORD)sizeof(buffer);
DWORD waitret = WaitForMultipleObjects(obj_count, objs,
FALSE, wait_timeout);
switch(waitret) {
@@ -1389,7 +1388,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
if(data->set.is_fread_set) {
size_t n;
/* read from user-supplied method */
- n = data->state.fread_func(buf, 1, buf_size, data->state.in);
+ n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
if(n == CURL_READFUNC_ABORT) {
keepon = FALSE;
result = CURLE_READ_ERROR;
@@ -1417,7 +1416,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
if(!readfile_read)
break;
- if(!ReadFile(stdin_handle, buf, buf_size,
+ if(!ReadFile(stdin_handle, buffer, buf_size,
&readfile_read, NULL)) {
keepon = FALSE;
result = CURLE_READ_ERROR;
@@ -1425,7 +1424,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
}
- result = send_telnet_data(data, buf, readfile_read);
+ result = send_telnet_data(data, buffer, readfile_read);
if(result) {
keepon = FALSE;
break;
@@ -1436,14 +1435,14 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
case WAIT_OBJECT_0 + 1:
{
- if(!ReadFile(stdin_handle, buf, buf_size,
+ if(!ReadFile(stdin_handle, buffer, buf_size,
&readfile_read, NULL)) {
keepon = FALSE;
result = CURLE_READ_ERROR;
break;
}
- result = send_telnet_data(data, buf, readfile_read);
+ result = send_telnet_data(data, buffer, readfile_read);
if(result) {
keepon = FALSE;
break;
@@ -1465,7 +1464,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
if(events.lNetworkEvents & FD_READ) {
/* read data from network */
- result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+ result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
/* read would've blocked. Loop again */
if(result == CURLE_AGAIN)
break;
@@ -1481,7 +1480,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
break;
}
- result = telrcv(data, (unsigned char *) buf, nread);
+ result = telrcv(data, (unsigned char *) buffer, nread);
if(result) {
keepon = FALSE;
break;
@@ -1542,11 +1541,11 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
case 0: /* timeout */
pfd[0].revents = 0;
pfd[1].revents = 0;
- /* FALLTHROUGH */
+ FALLTHROUGH();
default: /* read! */
if(pfd[0].revents & POLLIN) {
/* read data from network */
- result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+ result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
/* read would've blocked. Loop again */
if(result == CURLE_AGAIN)
break;
@@ -1572,7 +1571,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
total_dl += nread;
result = Curl_pgrsSetDownloadCounter(data, total_dl);
if(!result)
- result = telrcv(data, (unsigned char *)buf, nread);
+ result = telrcv(data, (unsigned char *)buffer, nread);
if(result) {
keepon = FALSE;
break;
@@ -1590,12 +1589,12 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
nread = 0;
if(poll_cnt == 2) {
if(pfd[1].revents & POLLIN) { /* read from in file */
- nread = read(pfd[1].fd, buf, data->set.buffer_size);
+ nread = read(pfd[1].fd, buffer, sizeof(buffer));
}
}
else {
/* read from user-supplied method */
- nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
+ nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
data->state.in);
if(nread == CURL_READFUNC_ABORT) {
keepon = FALSE;
@@ -1606,7 +1605,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
if(nread > 0) {
- result = send_telnet_data(data, buf, nread);
+ result = send_telnet_data(data, buffer, nread);
if(result) {
keepon = FALSE;
break;
diff --git a/libs/libcurl/src/tftp.c b/libs/libcurl/src/tftp.c
index 9f0877cbf2..d655a16de1 100644
--- a/libs/libcurl/src/tftp.c
+++ b/libs/libcurl/src/tftp.c
@@ -181,7 +181,7 @@ const struct Curl_handler Curl_handler_tftp = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
tftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_TFTP, /* defport */
diff --git a/libs/libcurl/src/transfer.c b/libs/libcurl/src/transfer.c
index 1fe0bbae06..bdfab6459e 100644
--- a/libs/libcurl/src/transfer.c
+++ b/libs/libcurl/src/transfer.c
@@ -413,321 +413,148 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc)
return TRUE;
}
+/**
+ * Receive raw response data for the transfer.
+ * @param data the transfer
+ * @param buf buffer to keep response data received
+ * @param blen length of `buf`
+ * @param eos_reliable if EOS detection in underlying connection is reliable
+ * @param err error code in case of -1 return
+ * @return number of bytes read or -1 for error
+ */
+static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
+ char *buf, size_t blen,
+ bool eos_reliable,
+ CURLcode *err)
+{
+ ssize_t nread;
+
+ DEBUGASSERT(blen > 0);
+ /* If we are reading BODY data and the connection does NOT handle EOF
+ * and we know the size of the BODY data, limit the read amount */
+ if(!eos_reliable && !data->req.header && data->req.size != -1) {
+ curl_off_t totalleft = data->req.size - data->req.bytecount;
+ if(totalleft <= 0)
+ blen = 0;
+ else if(totalleft < (curl_off_t)blen)
+ blen = (size_t)totalleft;
+ }
+
+ if(!blen) {
+ /* want nothing - continue as if read nothing. */
+ DEBUGF(infof(data, "readwrite_data: we're done"));
+ *err = CURLE_OK;
+ return 0;
+ }
+
+ *err = Curl_read(data, data->conn->sockfd, buf, blen, &nread);
+ if(*err)
+ return -1;
+ DEBUGASSERT(nread >= 0);
+ *err = CURLE_OK;
+ return nread;
+}
+
/*
* Go ahead and do a read if we have a readable socket or if
* the stream was rewound (in which case we have data in a
* buffer)
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
*/
static CURLcode readwrite_data(struct Curl_easy *data,
- struct connectdata *conn,
struct SingleRequest *k,
- int *didwhat, bool *done,
- bool *comeback)
+ int *didwhat, bool *done)
{
+ struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
char *buf;
size_t blen;
- size_t consumed;
- int maxloops = 100;
- curl_off_t max_recv = data->set.max_recv_speed?
- data->set.max_recv_speed : CURL_OFF_T_MAX;
- bool data_eof_handled = FALSE;
+ int maxloops = 10;
+ curl_off_t total_received = 0;
+ bool is_multiplex = FALSE;
DEBUGASSERT(data->state.buffer);
*done = FALSE;
- *comeback = FALSE;
/* This is where we loop until we have read everything there is to
read or we get a CURLE_AGAIN */
do {
- bool is_empty_data = FALSE;
- size_t bytestoread = data->set.buffer_size;
- /* For HTTP/2 and HTTP/3, read data without caring about the content
- length. This is safe because body in HTTP/2 is always segmented
- thanks to its framing layer. Meanwhile, we have to call Curl_read
- 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);
- data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
-
- /* Each loop iteration starts with a fresh buffer and handles
- * all data read into it. */
+ bool is_eos = FALSE;
+ size_t bytestoread;
+ ssize_t nread;
+
+ if(!is_multiplex) {
+ /* Multiplexed connection have inherent handling of EOF and we do not
+ * have to carefully restrict the amount we try to read.
+ * Multiplexed changes only in one direction. */
+ is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
+ }
+
buf = data->state.buffer;
- blen = 0;
-
- /* If we are reading BODY data and the connection does NOT handle EOF
- * and we know the size of the BODY data, limit the read amount */
- if(!k->header && !data_eof_handled && k->size != -1) {
- curl_off_t totalleft = k->size - k->bytecount;
- if(totalleft <= 0)
- bytestoread = 0;
- else if(totalleft < (curl_off_t)bytestoread)
- bytestoread = (size_t)totalleft;
+ bytestoread = data->set.buffer_size;
+
+ /* Observe any imposed speed limit */
+ if(bytestoread && data->set.max_recv_speed) {
+ curl_off_t net_limit = data->set.max_recv_speed - total_received;
+ if(net_limit <= 0)
+ break;
+ if((size_t)net_limit < bytestoread)
+ bytestoread = (size_t)net_limit;
}
- if(bytestoread) {
- /* receive data from the network! */
- ssize_t nread; /* number of bytes read */
- result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
+ nread = Curl_xfer_recv_resp(data, buf, bytestoread,
+ is_multiplex, &result);
+ if(nread < 0) {
if(CURLE_AGAIN == result) {
result = CURLE_OK;
break; /* get out of loop */
}
- else if(result)
- goto out;
- DEBUGASSERT(nread >= 0);
- blen = (size_t)nread;
- }
- else {
- /* read nothing but since we wanted nothing we consider this an OK
- situation to proceed from */
- DEBUGF(infof(data, "readwrite_data: we're done"));
- }
-
- if(!k->bytecount) {
- Curl_pgrsTime(data, TIMER_STARTTRANSFER);
- if(k->exp100 > EXP100_SEND_DATA)
- /* set time stamp to compare with when waiting for the 100 */
- k->start100 = Curl_now();
+ goto out; /* real error */
}
+ /* We only get a 0-length read on EndOfStream */
+ blen = (size_t)nread;
+ is_eos = (blen == 0);
*didwhat |= KEEP_RECV;
- /* indicates data of zero size, i.e. empty file */
- is_empty_data = ((blen == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
-
- if(0 < blen || is_empty_data) {
- /* data->state.buffer is allocated 1 byte larger than
- * data->set.buffer_size admits. *wink* */
- /* TODO: we should really not rely on this being 0-terminated, since
- * the actual data read might contain 0s. */
- buf[blen] = 0;
- }
if(!blen) {
/* 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)
+ if(is_multiplex)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
- k->keepon = 0; /* stop sending as well */
- if(!is_empty_data)
- break;
- }
-
- if(conn->handler->readwrite) {
- bool readmore = FALSE; /* indicates data is incomplete, need more */
- consumed = 0;
- result = conn->handler->readwrite(data, conn, buf, blen,
- &consumed, &readmore);
- if(result)
- goto out;
- if(readmore)
- break;
- buf += consumed;
- blen -= consumed;
- if(k->download_done) {
- /* We've stopped dealing with input, get out of the do-while loop */
- if(blen > 0) {
- infof(data,
- "Excess found:"
- " excess = %zu"
- " url = %s (zero-length body)",
- blen, data->state.up.path);
- }
-
- /* we make sure that this socket isn't read more now */
- k->keepon &= ~KEEP_RECV;
+ if(k->eos_written) { /* already did write this to client, leave */
+ k->keepon = 0; /* stop sending as well */
break;
}
}
+ total_received += blen;
-#ifndef CURL_DISABLE_HTTP
- /* Since this is a two-state thing, we check if we are parsing
- headers at the moment or not. */
- if(k->header) {
- consumed = 0;
- result = Curl_http_readwrite_headers(data, conn, buf, blen, &consumed);
- if(result)
- goto out;
- buf += consumed;
- blen -= consumed;
-
- if(conn->handler->readwrite &&
- (k->maxdownload <= 0 && blen > 0)) {
- bool readmore = FALSE; /* indicates data is incomplete, need more */
- consumed = 0;
- result = conn->handler->readwrite(data, conn, buf, blen,
- &consumed, &readmore);
- if(result)
- goto out;
- if(readmore)
- break;
- buf += consumed;
- blen -= consumed;
- }
-
- if(k->download_done) {
- /* We've stopped dealing with input, get out of the do-while loop */
- if(blen > 0) {
- infof(data,
- "Excess found:"
- " excess = %zu"
- " url = %s (zero-length body)",
- blen, data->state.up.path);
- }
-
- /* we make sure that this socket isn't read more now */
- k->keepon &= ~KEEP_RECV;
- break;
- }
- }
-#endif /* CURL_DISABLE_HTTP */
-
-
- /* This is not an 'else if' since it may be a rest from the header
- parsing, where the beginning of the buffer is headers and the end
- is non-headers. */
- if(!k->header && (blen > 0 || is_empty_data)) {
-
- if(data->req.no_body && blen > 0) {
- /* data arrives although we want none, bail out */
- streamclose(conn, "ignoring body");
- DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
- blen));
- *done = TRUE;
- result = CURLE_WEIRD_SERVER_REPLY;
- goto out;
- }
-
-#ifndef CURL_DISABLE_HTTP
- if(0 == k->bodywrites && !is_empty_data) {
- /* These checks are only made the first time we are about to
- write a piece of the body */
- if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
- /* HTTP-only checks */
- result = Curl_http_firstwrite(data, conn, done);
- if(result || *done)
- goto out;
- }
- } /* this is the first time we write a body part */
-#endif /* CURL_DISABLE_HTTP */
-
-#ifndef CURL_DISABLE_HTTP
- if(k->chunk) {
- /*
- * Here comes a chunked transfer flying and we need to decode this
- * properly. While the name says read, this function both reads
- * and writes away the data.
- */
- CURLcode extra;
- CHUNKcode res;
-
- consumed = 0;
- res = Curl_httpchunk_read(data, buf, blen, &consumed, &extra);
-
- if(CHUNKE_OK < res) {
- if(CHUNKE_PASSTHRU_ERROR == res) {
- failf(data, "Failed reading the chunked-encoded stream");
- result = extra;
- goto out;
- }
- failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
- result = CURLE_RECV_ERROR;
- goto out;
- }
-
- buf += consumed;
- blen -= consumed;
- if(CHUNKE_STOP == res) {
- /* we're done reading chunks! */
- k->keepon &= ~KEEP_RECV; /* read no more */
- /* chunks read successfully, download is complete */
- k->download_done = TRUE;
-
- /* N number of bytes at the end of the str buffer that weren't
- written to the client. */
- if(conn->chunk.datasize) {
- infof(data, "Leftovers after chunking: % "
- CURL_FORMAT_CURL_OFF_T "u bytes",
- conn->chunk.datasize);
- }
- }
- /* If it returned OK, we just keep going */
- }
-#endif /* CURL_DISABLE_HTTP */
-
- max_recv -= blen;
-
- if(!k->chunk && (blen || k->badheader || is_empty_data)) {
- /* If this is chunky transfer, it was already written */
-
- if(k->badheader) {
- /* we parsed a piece of data wrongly assuming it was a header
- and now we output it as body instead */
- size_t headlen = Curl_dyn_len(&data->state.headerb);
-
- /* Don't let excess data pollute body writes */
- if(k->maxdownload != -1 && (curl_off_t)headlen > k->maxdownload)
- headlen = (size_t)k->maxdownload;
-
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&data->state.headerb),
- headlen);
- if(result)
- goto out;
- }
-
- if(blen) {
-#ifndef CURL_DISABLE_POP3
- if(conn->handler->protocol & PROTO_FAMILY_POP3) {
- result = k->ignorebody? CURLE_OK :
- Curl_pop3_write(data, buf, blen);
- }
- else
-#endif /* CURL_DISABLE_POP3 */
- result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
- }
- k->badheader = FALSE; /* taken care of now */
-
- if(result)
- goto out;
- }
-
- if(k->download_done && !is_http3) {
- /* HTTP/3 over QUIC should keep reading until QUIC connection
- is closed. In contrast to HTTP/2 which can stop reading
- from TCP connection, HTTP/3 over QUIC needs ACK from server
- to ensure stream closure. It should keep reading. */
- k->keepon &= ~KEEP_RECV; /* we're done reading */
- }
- } /* if(!header and data to read) */
+ result = Curl_xfer_write_resp(data, buf, blen, is_eos, done);
+ if(result || *done)
+ goto out;
- if(is_empty_data) {
- /* if we received nothing, the server closed the connection and we
- are done */
- k->keepon &= ~KEEP_RECV;
- k->download_done = TRUE;
+ /* if we are done, we stop receiving. On multiplexed connections,
+ * we should read the EOS. Which may arrive as meta data after
+ * the bytes. Not taking it in might lead to RST of streams. */
+ if((!is_multiplex && data->req.download_done) || is_eos) {
+ data->req.keepon &= ~KEEP_RECV;
}
-
- if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
- /* this is a paused or stopped transfer */
+ /* if we are PAUSEd or stopped receiving, leave the loop */
+ if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV))
break;
- }
- } while((max_recv > 0) && data_pending(data) && maxloops--);
+ } while(maxloops-- && data_pending(data));
- if(maxloops <= 0 || max_recv <= 0) {
- /* we mark it as read-again-please */
- data->state.dselect_bits = CURL_CSELECT_IN;
- *comeback = TRUE;
+ if(maxloops <= 0) {
+ /* did not read until EAGAIN, mark read-again-please */
+ data->state.select_bits = CURL_CSELECT_IN;
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ data->state.select_bits |= CURL_CSELECT_OUT;
}
if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
- (conn->bits.close || data_eof_handled)) {
+ (conn->bits.close || is_multiplex)) {
/* 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. */
@@ -1023,46 +850,41 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits)
* of our state machine are handling PAUSED transfers correctly. So, we
* do not want to go there.
* NOTE: we are only interested in PAUSE, not HOLD. */
- return (((select_bits & CURL_CSELECT_IN) &&
- (data->req.keepon & KEEP_RECV_PAUSE)) ||
- ((select_bits & CURL_CSELECT_OUT) &&
- (data->req.keepon & KEEP_SEND_PAUSE)));
+
+ /* if there is data in a direction not paused, return false */
+ if(((select_bits & CURL_CSELECT_IN) &&
+ !(data->req.keepon & KEEP_RECV_PAUSE)) ||
+ ((select_bits & CURL_CSELECT_OUT) &&
+ !(data->req.keepon & KEEP_SEND_PAUSE)))
+ return FALSE;
+
+ return (data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE));
}
/*
* Curl_readwrite() is the low-level function to be called when data is to
* be read and written to/from the connection.
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
*/
-CURLcode Curl_readwrite(struct connectdata *conn,
- struct Curl_easy *data,
- bool *done,
- bool *comeback)
+CURLcode Curl_readwrite(struct Curl_easy *data,
+ bool *done)
{
+ struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
CURLcode result;
struct curltime now;
int didwhat = 0;
int select_bits;
- if(data->state.dselect_bits) {
- if(select_bits_paused(data, data->state.dselect_bits)) {
+ if(data->state.select_bits) {
+ if(select_bits_paused(data, data->state.select_bits)) {
/* leave the bits unchanged, so they'll tell us what to do when
* this transfer gets unpaused. */
- DEBUGF(infof(data, "readwrite, dselect_bits, early return on PAUSED"));
+ DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED"));
result = CURLE_OK;
goto out;
}
- select_bits = data->state.dselect_bits;
- data->state.dselect_bits = 0;
- }
- else if(conn->cselect_bits) {
- /* CAVEAT: adding `select_bits_paused()` check here makes test640 hang
- * (among others). Which hints at strange state handling in FTP land... */
- select_bits = conn->cselect_bits;
- conn->cselect_bits = 0;
+ select_bits = data->state.select_bits;
+ data->state.select_bits = 0;
}
else {
curl_socket_t fd_read;
@@ -1100,7 +922,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
the stream was rewound (in which case we have data in a
buffer) */
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
- result = readwrite_data(data, conn, k, &didwhat, done, comeback);
+ result = readwrite_data(data, k, &didwhat, done);
if(result || *done)
goto out;
}
@@ -1196,21 +1018,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
result = CURLE_PARTIAL_FILE;
goto out;
}
- if(!(data->req.no_body) && k->chunk &&
- (conn->chunk.state != CHUNK_STOP)) {
- /*
- * In chunked mode, return an error if the connection is closed prior to
- * the empty (terminating) chunk is read.
- *
- * The condition above used to check for
- * conn->proto.http->chunk.datasize != 0 which is true after reading
- * *any* chunk, not just the empty chunk.
- *
- */
- failf(data, "transfer closed with outstanding read data remaining");
- result = CURLE_PARTIAL_FILE;
- goto out;
- }
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
goto out;
@@ -1225,52 +1032,6 @@ out:
return result;
}
-/*
- * Curl_single_getsock() gets called by the multi interface code when the app
- * has requested to get the sockets for the current connection. This function
- * will then be called once for every connection that the multi interface
- * keeps track of. This function will only be called for connections that are
- * in the proper state to have this information available.
- */
-int Curl_single_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *sock)
-{
- int bitmap = GETSOCK_BLANK;
- unsigned sockindex = 0;
-
- if(conn->handler->perform_getsock)
- return conn->handler->perform_getsock(data, conn, sock);
-
- /* don't include HOLD and PAUSE connections */
- if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
-
- DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
-
- bitmap |= GETSOCK_READSOCK(sockindex);
- sock[sockindex] = conn->sockfd;
- }
-
- /* don't include HOLD and PAUSE connections */
- if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
- if((conn->sockfd != conn->writesockfd) ||
- bitmap == GETSOCK_BLANK) {
- /* only if they are not the same socket and we have a readable
- one, we increase index */
- if(bitmap != GETSOCK_BLANK)
- sockindex++; /* increase index if we need two entries */
-
- DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
-
- sock[sockindex] = conn->writesockfd;
- }
-
- bitmap |= GETSOCK_WRITESOCK(sockindex);
- }
-
- return bitmap;
-}
-
/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
which means this gets called once for each subsequent redirect etc */
void Curl_init_CONNECT(struct Curl_easy *data)
@@ -1917,3 +1678,41 @@ Curl_setup_transfer(
} /* if(k->getheader || !data->req.no_body) */
}
+
+CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
+ char *buf, size_t blen,
+ bool is_eos, bool *done)
+{
+ CURLcode result = CURLE_OK;
+
+ if(data->conn->handler->write_resp) {
+ /* protocol handlers offering this function take full responsibility
+ * for writing all received download data to the client. */
+ result = data->conn->handler->write_resp(data, buf, blen, is_eos, done);
+ }
+ else {
+ /* No special handling by protocol handler, write all received data
+ * as BODY to the client. */
+ if(blen || is_eos) {
+ int cwtype = CLIENTWRITE_BODY;
+ if(is_eos)
+ cwtype |= CLIENTWRITE_EOS;
+
+#ifndef CURL_DISABLE_POP3
+ if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) {
+ result = data->req.ignorebody? CURLE_OK :
+ Curl_pop3_write(data, buf, blen);
+ }
+ else
+#endif /* CURL_DISABLE_POP3 */
+ result = Curl_client_write(data, cwtype, buf, blen);
+ }
+ }
+
+ if(!result && is_eos) {
+ /* If we wrote the EOS, we are definitely done */
+ data->req.eos_written = TRUE;
+ data->req.download_done = TRUE;
+ }
+ return result;
+}
diff --git a/libs/libcurl/src/transfer.h b/libs/libcurl/src/transfer.h
index 612fca5edd..b269f7a44f 100644
--- a/libs/libcurl/src/transfer.h
+++ b/libs/libcurl/src/transfer.h
@@ -45,9 +45,7 @@ typedef enum {
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
followtype type);
-CURLcode Curl_readwrite(struct connectdata *conn,
- struct Curl_easy *data, bool *done,
- bool *comeback);
+CURLcode Curl_readwrite(struct Curl_easy *data, bool *done);
int Curl_single_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
@@ -59,6 +57,23 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k);
+/**
+ * Write the transfer raw response bytes, as received from the connection.
+ * Will handle all passed bytes or return an error. By default, this will
+ * write the bytes as BODY to the client. Protocols may provide a
+ * "write_resp" callback in their handler to add specific treatment. E.g.
+ * HTTP parses response headers and passes them differently to the client.
+ * @param data the transfer
+ * @param buf the raw response bytes
+ * @param blen the amount of bytes in `buf`
+ * @param is_eos TRUE iff the connection indicates this to be the last
+ * bytes of the response
+ * @param done on returnm, TRUE iff the response is complete
+ */
+CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
+ char *buf, size_t blen,
+ bool is_eos, bool *done);
+
/* This sets up a forthcoming transfer */
void
Curl_setup_transfer (struct Curl_easy *data,
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c
index 243a603c8b..eab74ab2c0 100644
--- a/libs/libcurl/src/url.c
+++ b/libs/libcurl/src/url.c
@@ -298,6 +298,10 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
+ /* this destroys the channel and we cannot use it anymore after this */
+ Curl_resolver_cancel(data);
+ Curl_resolver_cleanup(data->state.async.resolver);
+
data_priority_cleanup(data);
/* No longer a dirty share, if it exists */
@@ -430,11 +434,13 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
/* Set the default CA cert bundle/path detected/specified at build time.
*
- * If Schannel is the selected SSL backend then these locations are
- * ignored. We allow setting CA location for schannel only when explicitly
- * specified by the user via CURLOPT_CAINFO / --cacert.
+ * If Schannel or SecureTransport is the selected SSL backend then these
+ * locations are ignored. We allow setting CA location for schannel and
+ * securetransport when explicitly specified by the user via
+ * CURLOPT_CAINFO / --cacert.
*/
- if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
+ if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL &&
+ Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) {
#if defined(CURL_CA_BUNDLE)
result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
if(result)
@@ -514,6 +520,13 @@ CURLcode Curl_open(struct Curl_easy **curl)
data->magic = CURLEASY_MAGIC_NUMBER;
+ result = Curl_resolver_init(data, &data->state.async.resolver);
+ if(result) {
+ DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
+ free(data);
+ return result;
+ }
+
result = Curl_init_userdefined(data);
if(!result) {
Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
@@ -530,6 +543,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
}
if(result) {
+ Curl_resolver_cleanup(data->state.async.resolver);
Curl_dyn_free(&data->state.headerb);
Curl_freeset(data);
free(data);
@@ -563,7 +577,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_conn_cf_discard_all(data, conn, (int)i);
}
- Curl_resolver_cleanup(conn->resolve_async.resolver);
Curl_free_idnconverted_hostname(&conn->host);
Curl_free_idnconverted_hostname(&conn->conn_to_host);
#ifndef CURL_DISABLE_PROXY
@@ -581,9 +594,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->sasl_authzid);
Curl_safefree(conn->options);
Curl_safefree(conn->oauth_bearer);
-#ifndef CURL_DISABLE_HTTP
- Curl_dyn_free(&conn->trailer);
-#endif
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
Curl_safefree(conn->hostname_resolve);
@@ -663,7 +673,6 @@ void Curl_disconnect(struct Curl_easy *data,
conn->handler->disconnect(data, conn, dead_connection);
conn_shutdown(data);
- Curl_resolver_cancel(data);
/* detach it again */
Curl_detach_connection(data);
@@ -1346,6 +1355,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->sockfd = CURL_SOCKET_BAD;
+ conn->writesockfd = CURL_SOCKET_BAD;
conn->connection_id = -1; /* no ID */
conn->port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown at this point */
@@ -1680,8 +1691,9 @@ static CURLcode findprotocol(struct Curl_easy *data,
/* The protocol was not found in the table, but we don't have to assign it
to anything since it is already assigned to a dummy-struct in the
create_conn() function when the connectdata struct is allocated. */
- failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME,
- protostr);
+ failf(data, "Protocol \"%s\" %s%s", protostr,
+ p ? "disabled" : "not supported",
+ data->state.this_is_a_follow ? " (in redirect)":"");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@@ -3739,35 +3751,7 @@ static CURLcode create_conn(struct Curl_easy *data,
goto out;
}
- result = Curl_resolver_init(data, &conn->resolve_async.resolver);
- if(result) {
- DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
- goto out;
- }
-
Curl_attach_connection(data, conn);
-
-#ifdef USE_ARES
- result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
- if(result && result != CURLE_NOT_BUILT_IN)
- goto out;
-
- result = Curl_set_dns_interface(data,
- data->set.str[STRING_DNS_INTERFACE]);
- if(result && result != CURLE_NOT_BUILT_IN)
- goto out;
-
- result = Curl_set_dns_local_ip4(data,
- data->set.str[STRING_DNS_LOCAL_IP4]);
- if(result && result != CURLE_NOT_BUILT_IN)
- goto out;
-
- result = Curl_set_dns_local_ip6(data,
- data->set.str[STRING_DNS_LOCAL_IP6]);
- if(result && result != CURLE_NOT_BUILT_IN)
- goto out;
-#endif /* USE_ARES */
-
result = Curl_conncache_add_conn(data);
if(result)
goto out;
diff --git a/libs/libcurl/src/urlapi.c b/libs/libcurl/src/urlapi.c
index 1a94e7f4cf..70b63983f7 100644
--- a/libs/libcurl/src/urlapi.c
+++ b/libs/libcurl/src/urlapi.c
@@ -126,6 +126,9 @@ static const char *find_host_sep(const char *url)
return sep < query ? sep : query;
}
+/* convert CURLcode to CURLUcode */
+#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \
+ CURLUE_OUT_OF_MEMORY)
/*
* Decide whether a character in a URL must be escaped.
*/
@@ -146,6 +149,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
bool left = !query;
const unsigned char *iptr;
const unsigned char *host_sep = (const unsigned char *) url;
+ CURLcode result;
if(!relative)
host_sep = (const unsigned char *) find_host_sep(url);
@@ -154,20 +158,19 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
len; iptr++, len--) {
if(iptr < host_sep) {
- if(Curl_dyn_addn(o, iptr, 1))
- return CURLUE_OUT_OF_MEMORY;
+ result = Curl_dyn_addn(o, iptr, 1);
+ if(result)
+ return cc2cu(result);
continue;
}
if(*iptr == ' ') {
- if(left) {
- if(Curl_dyn_addn(o, "%20", 3))
- return CURLUE_OUT_OF_MEMORY;
- }
- else {
- if(Curl_dyn_addn(o, "+", 1))
- return CURLUE_OUT_OF_MEMORY;
- }
+ if(left)
+ result = Curl_dyn_addn(o, "%20", 3);
+ else
+ result = Curl_dyn_addn(o, "+", 1);
+ if(result)
+ return cc2cu(result);
continue;
}
@@ -178,13 +181,12 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
char out[3]={'%'};
out[1] = hexdigits[*iptr>>4];
out[2] = hexdigits[*iptr & 0xf];
- if(Curl_dyn_addn(o, out, 3))
- return CURLUE_OUT_OF_MEMORY;
- }
- else {
- if(Curl_dyn_addn(o, iptr, 1))
- return CURLUE_OUT_OF_MEMORY;
+ result = Curl_dyn_addn(o, out, 3);
}
+ else
+ result = Curl_dyn_addn(o, iptr, 1);
+ if(result)
+ return cc2cu(result);
}
return CURLUE_OK;
@@ -248,7 +250,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
*
* Note that this function destroys the 'base' string.
*/
-static char *concat_url(char *base, const char *relurl)
+static CURLcode concat_url(char *base, const char *relurl, char **newurl)
{
/***
TRY to append this new path to the old URL
@@ -260,6 +262,9 @@ static char *concat_url(char *base, const char *relurl)
char *pathsep;
bool host_changed = FALSE;
const char *useurl = relurl;
+ CURLcode result = CURLE_OK;
+ CURLUcode uc;
+ *newurl = NULL;
/* protsep points to the start of the host name */
protsep = strstr(base, "//");
@@ -360,21 +365,27 @@ static char *concat_url(char *base, const char *relurl)
Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
/* copy over the root url part */
- if(Curl_dyn_add(&newest, base))
- return NULL;
+ result = Curl_dyn_add(&newest, base);
+ if(result)
+ return result;
/* check if we need to append a slash */
if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
;
else {
- if(Curl_dyn_addn(&newest, "/", 1))
- return NULL;
+ result = Curl_dyn_addn(&newest, "/", 1);
+ if(result)
+ return result;
}
/* then append the new piece on the right side */
- urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE);
+ uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed,
+ FALSE);
+ if(uc)
+ return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY;
- return Curl_dyn_ptr(&newest);
+ *newurl = Curl_dyn_ptr(&newest);
+ return CURLE_OK;
}
/* scan for byte values <= 31, 127 and sometimes space */
@@ -712,24 +723,30 @@ static int ipv4_normalize(struct dynbuf *host)
Curl_dyn_reset(host);
result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- parts[0] >> 24, (parts[0] >> 16) & 0xff,
- (parts[0] >> 8) & 0xff, parts[0] & 0xff);
+ (unsigned int)(parts[0] >> 24),
+ (unsigned int)((parts[0] >> 16) & 0xff),
+ (unsigned int)((parts[0] >> 8) & 0xff),
+ (unsigned int)(parts[0] & 0xff));
break;
case 1: /* a.b -- 8.24 bits */
if((parts[0] > 0xff) || (parts[1] > 0xffffff))
return HOST_NAME;
Curl_dyn_reset(host);
result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- parts[0], (parts[1] >> 16) & 0xff,
- (parts[1] >> 8) & 0xff, parts[1] & 0xff);
+ (unsigned int)(parts[0]),
+ (unsigned int)((parts[1] >> 16) & 0xff),
+ (unsigned int)((parts[1] >> 8) & 0xff),
+ (unsigned int)(parts[1] & 0xff));
break;
case 2: /* a.b.c -- 8.8.16 bits */
if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
return HOST_NAME;
Curl_dyn_reset(host);
result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- parts[0], parts[1], (parts[2] >> 8) & 0xff,
- parts[2] & 0xff);
+ (unsigned int)(parts[0]),
+ (unsigned int)(parts[1]),
+ (unsigned int)((parts[2] >> 8) & 0xff),
+ (unsigned int)(parts[2] & 0xff));
break;
case 3: /* a.b.c.d -- 8.8.8.8 bits */
if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
@@ -737,7 +754,10 @@ static int ipv4_normalize(struct dynbuf *host)
return HOST_NAME;
Curl_dyn_reset(host);
result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- parts[0], parts[1], parts[2], parts[3]);
+ (unsigned int)(parts[0]),
+ (unsigned int)(parts[1]),
+ (unsigned int)(parts[2]),
+ (unsigned int)(parts[3]));
break;
}
if(result)
@@ -766,7 +786,7 @@ static CURLUcode urldecode_host(struct dynbuf *host)
result = Curl_dyn_addn(host, decoded, dlen);
free(decoded);
if(result)
- return CURLUE_OUT_OF_MEMORY;
+ return cc2cu(result);
}
return CURLUE_OK;
@@ -779,22 +799,24 @@ static CURLUcode parse_authority(struct Curl_URL *u,
bool has_scheme)
{
size_t offset;
- CURLUcode result;
+ CURLUcode uc;
+ CURLcode result;
/*
* Parse the login details and strip them out of the host name.
*/
- result = parse_hostname_login(u, auth, authlen, flags, &offset);
- if(result)
+ uc = parse_hostname_login(u, auth, authlen, flags, &offset);
+ if(uc)
goto out;
- if(Curl_dyn_addn(host, auth + offset, authlen - offset)) {
- result = CURLUE_OUT_OF_MEMORY;
+ result = Curl_dyn_addn(host, auth + offset, authlen - offset);
+ if(result) {
+ uc = cc2cu(result);
goto out;
}
- result = Curl_parse_port(u, host, has_scheme);
- if(result)
+ uc = Curl_parse_port(u, host, has_scheme);
+ if(uc)
goto out;
if(!Curl_dyn_len(host))
@@ -804,24 +826,24 @@ static CURLUcode parse_authority(struct Curl_URL *u,
case HOST_IPV4:
break;
case HOST_IPV6:
- result = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+ uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
break;
case HOST_NAME:
- result = urldecode_host(host);
- if(!result)
- result = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+ uc = urldecode_host(host);
+ if(!uc)
+ uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
break;
case HOST_ERROR:
- result = CURLUE_OUT_OF_MEMORY;
+ uc = CURLUE_OUT_OF_MEMORY;
break;
case HOST_BAD:
default:
- result = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */
+ uc = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */
break;
}
out:
- return result;
+ return uc;
}
CURLUcode Curl_url_set_authority(CURLU *u, const char *authority,
@@ -1070,8 +1092,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
len = path - ptr;
if(len) {
- if(Curl_dyn_addn(&host, ptr, len)) {
- result = CURLUE_OUT_OF_MEMORY;
+ CURLcode code = Curl_dyn_addn(&host, ptr, len);
+ if(code) {
+ result = cc2cu(code);
goto fail;
}
uncpath = TRUE;
@@ -1224,14 +1247,13 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(flags & CURLU_URLENCODE) {
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
- if(urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE)) {
- result = CURLUE_OUT_OF_MEMORY;
+ result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE);
+ if(result)
goto fail;
- }
u->fragment = Curl_dyn_ptr(&enc);
}
else {
- u->fragment = Curl_strndup(fragment + 1, fraglen - 1);
+ u->fragment = Curl_memdup0(fragment + 1, fraglen - 1);
if(!u->fragment) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
@@ -1242,7 +1264,6 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
pathlen -= fraglen;
}
- DEBUGASSERT(pathlen < urllen);
query = memchr(path, '?', pathlen);
if(query) {
size_t qlen = fragment ? (size_t)(fragment - query) :
@@ -1253,14 +1274,13 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
/* skip the leading question mark */
- if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) {
- result = CURLUE_OUT_OF_MEMORY;
+ result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE);
+ if(result)
goto fail;
- }
u->query = Curl_dyn_ptr(&enc);
}
else {
- u->query = Curl_strndup(query + 1, qlen - 1);
+ u->query = Curl_memdup0(query + 1, qlen - 1);
if(!u->query) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
@@ -1280,10 +1300,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(pathlen && (flags & CURLU_URLENCODE)) {
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
- if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) {
- result = CURLUE_OUT_OF_MEMORY;
+ result = urlencode_str(&enc, path, pathlen, TRUE, FALSE);
+ if(result)
goto fail;
- }
pathlen = Curl_dyn_len(&enc);
path = u->path = Curl_dyn_ptr(&enc);
}
@@ -1294,7 +1313,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
}
else {
if(!u->path) {
- u->path = Curl_strndup(path, pathlen);
+ u->path = Curl_memdup0(path, pathlen);
if(!u->path) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
@@ -1592,7 +1611,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
if(ptr) {
size_t partlen = strlen(ptr);
size_t i = 0;
- *part = Curl_strndup(ptr, partlen);
+ *part = Curl_memdup0(ptr, partlen);
if(!*part)
return CURLUE_OUT_OF_MEMORY;
if(plusdecode) {
@@ -1619,10 +1638,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
}
if(urlencode) {
struct dynbuf enc;
+ CURLUcode uc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
- if(urlencode_str(&enc, *part, partlen, TRUE,
- what == CURLUPART_QUERY))
- return CURLUE_OUT_OF_MEMORY;
+ uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY);
+ if(uc)
+ return uc;
free(*part);
*part = Curl_dyn_ptr(&enc);
}
@@ -1807,7 +1827,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
* If the existing contents is enough for a URL, allow a relative URL to
* replace it.
*/
- CURLUcode result;
+ CURLcode result;
+ CURLUcode uc;
char *oldurl;
char *redired_url;
@@ -1827,14 +1848,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
/* apply the relative part to create a new URL
* and replace the existing one with it. */
- redired_url = concat_url(oldurl, part);
+ result = concat_url(oldurl, part, &redired_url);
free(oldurl);
- if(!redired_url)
- return CURLUE_OUT_OF_MEMORY;
+ if(result)
+ return cc2cu(result);
- result = parseurl_and_replace(redired_url, u, flags);
+ uc = parseurl_and_replace(redired_url, u, flags);
free(redired_url);
- return result;
+ return uc;
}
default:
return CURLUE_UNKNOWN_PART;
@@ -1848,7 +1869,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
if(leadingslash && (part[0] != '/')) {
CURLcode result = Curl_dyn_addn(&enc, "/", 1);
if(result)
- return CURLUE_OUT_OF_MEMORY;
+ return cc2cu(result);
}
if(urlencode) {
const unsigned char *i;
@@ -1868,7 +1889,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
equalsencode = FALSE;
result = Curl_dyn_addn(&enc, i, 1);
if(result)
- return CURLUE_OUT_OF_MEMORY;
+ return cc2cu(result);
}
else {
char out[3]={'%'};
@@ -1876,7 +1897,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
out[2] = hexdigits[*i & 0xf];
result = Curl_dyn_addn(&enc, out, 3);
if(result)
- return CURLUE_OUT_OF_MEMORY;
+ return cc2cu(result);
}
}
}
@@ -1884,7 +1905,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
char *p;
CURLcode result = Curl_dyn_add(&enc, part);
if(result)
- return CURLUE_OUT_OF_MEMORY;
+ return cc2cu(result);
p = Curl_dyn_ptr(&enc);
while(*p) {
/* make sure percent encoded are lower case */
diff --git a/libs/libcurl/src/urldata.h b/libs/libcurl/src/urldata.h
index b5b6bf866b..b49e87bdd5 100644
--- a/libs/libcurl/src/urldata.h
+++ b/libs/libcurl/src/urldata.h
@@ -583,7 +583,7 @@ struct hostname {
(((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND)
/* transfer receive is not on PAUSE or HOLD */
#define CURL_WANT_RECV(data) \
- (!((data)->req.keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD)))
+ (((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV)
#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
#define USE_CURL_ASYNC
@@ -683,7 +683,8 @@ struct SingleRequest {
enum expect100 exp100; /* expect 100 continue state */
enum upgrade101 upgr101; /* 101 upgrade state */
- /* Content unencoding stack. See sec 3.5, RFC2616. */
+ /* Client Writer stack, handles trasnfer- and content-encodings, protocol
+ * checks, pausing by client callbacks. */
struct Curl_cwriter *writer_stack;
time_t timeofdoc;
long bodywrites;
@@ -730,11 +731,10 @@ struct SingleRequest {
#ifndef CURL_DISABLE_COOKIES
unsigned char setcookies;
#endif
- unsigned char writer_stack_depth; /* Unencoding stack depth. */
BIT(header); /* incoming data has HTTP header */
- BIT(badheader); /* header parsing found sth not a header */
BIT(content_range); /* set TRUE if Content-Range: was found */
BIT(download_done); /* set to TRUE when download is complete */
+ BIT(eos_written); /* iff EOS has been written to client */
BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
upload and we're uploading the last chunk */
BIT(ignorebody); /* we read a response-body but we ignore it! */
@@ -816,10 +816,10 @@ struct Curl_handler {
bool dead_connection);
/* If used, this function gets called from transfer.c:readwrite_data() to
- allow the protocol to do extra reads/writes */
- CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
- const char *buf, size_t blen,
- size_t *pconsumed, bool *readmore);
+ allow the protocol to do extra handling in writing response to
+ the client. */
+ CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+ bool is_eos, bool *done);
/* This function can perform various checks on the connection. See
CONNCHECK_* for more information about the checks that can be performed,
@@ -898,11 +898,6 @@ struct ldapconninfo;
struct connectdata {
struct Curl_llist_element bundle_node; /* conncache */
- /* chunk is for HTTP chunked encoding, but is in the general connectdata
- struct only because we can do just about any protocol through an HTTP
- proxy and an HTTP proxy may in fact respond using chunked encoding */
- struct Curl_chunker chunk;
-
curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
void *closesocket_client;
@@ -921,9 +916,6 @@ struct connectdata {
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;
-#ifdef USE_CURL_ASYNC
- struct Curl_async resolve_async; /* asynchronous name resolver data */
-#endif
/* 'remote_addr' is the particular IP we connected to. it is owned, set
* and NULLed by the connected socket filter (if there is one). */
@@ -1028,11 +1020,6 @@ struct connectdata {
struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
#endif
-#ifndef CURL_DISABLE_HTTP
- /* for chunked-encoded trailer */
- struct dynbuf trailer;
-#endif
-
union {
#ifndef CURL_DISABLE_FTP
struct ftp_conn ftpc;
@@ -1103,7 +1090,6 @@ struct connectdata {
unsigned short localport;
unsigned short secondary_port; /* secondary socket remote port to connect to
(ftp) */
- unsigned char cselect_bits; /* bitmask of socket events */
unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
value */
#ifndef CURL_DISABLE_PROXY
@@ -1193,6 +1179,7 @@ struct Progress {
curl_off_t dlspeed;
curl_off_t ulspeed;
+ timediff_t t_postqueue;
timediff_t t_nslookup;
timediff_t t_connect;
timediff_t t_appconnect;
@@ -1382,6 +1369,9 @@ struct UrlState {
#endif
struct auth authhost; /* auth details for host */
struct auth authproxy; /* auth details for proxy */
+#ifdef USE_CURL_ASYNC
+ struct Curl_async async; /* asynchronous name resolver data */
+#endif
#if defined(USE_OPENSSL)
/* void instead of ENGINE to avoid bleeding OpenSSL into this header */
@@ -1479,7 +1469,7 @@ struct UrlState {
server involved in this request */
unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
is this */
- unsigned char dselect_bits; /* != 0 -> bitmask of socket events for this
+ unsigned char select_bits; /* != 0 -> bitmask of socket events for this
transfer overriding anything the socket may
report */
#ifdef CURLDEBUG
diff --git a/libs/libcurl/src/vauth/digest_sspi.c b/libs/libcurl/src/vauth/digest_sspi.c
index 996b83d50e..d61c8386bc 100644
--- a/libs/libcurl/src/vauth/digest_sspi.c
+++ b/libs/libcurl/src/vauth/digest_sspi.c
@@ -211,8 +211,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
infof(data, "schannel: InitializeSecurityContext failed: %s",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+#endif
return CURLE_AUTH_ERROR;
}
@@ -603,8 +605,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
if(status == SEC_E_INSUFFICIENT_MEMORY)
return CURLE_OUT_OF_MEMORY;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
infof(data, "schannel: InitializeSecurityContext failed: %s",
Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+#endif
return CURLE_AUTH_ERROR;
}
diff --git a/libs/libcurl/src/vauth/krb5_gssapi.c b/libs/libcurl/src/vauth/krb5_gssapi.c
index 2b7b21534e..655f908300 100644
--- a/libs/libcurl/src/vauth/krb5_gssapi.c
+++ b/libs/libcurl/src/vauth/krb5_gssapi.c
@@ -226,7 +226,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
/* Extract the security layer and the maximum message size */
indata = output_token.value;
sec_layer = indata[0];
- max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+ max_size = ((unsigned int)indata[1] << 16) |
+ ((unsigned int)indata[2] << 8) | indata[3];
/* Free the challenge as it is not required anymore */
gss_release_buffer(&unused_status, &output_token);
diff --git a/libs/libcurl/src/vauth/krb5_sspi.c b/libs/libcurl/src/vauth/krb5_sspi.c
index fe2fdb3732..78999232e3 100644
--- a/libs/libcurl/src/vauth/krb5_sspi.c
+++ b/libs/libcurl/src/vauth/krb5_sspi.c
@@ -319,7 +319,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
/* Extract the security layer and the maximum message size */
indata = input_buf[1].pvBuffer;
sec_layer = indata[0];
- max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+ max_size = ((unsigned long)indata[1] << 16) |
+ ((unsigned long)indata[2] << 8) | indata[3];
/* Free the challenge as it is not required anymore */
s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
diff --git a/libs/libcurl/src/vauth/ntlm.c b/libs/libcurl/src/vauth/ntlm.c
index 0cdf1a159f..a434497d42 100644
--- a/libs/libcurl/src/vauth/ntlm.c
+++ b/libs/libcurl/src/vauth/ntlm.c
@@ -44,6 +44,7 @@
#include "warnless.h"
#include "rand.h"
#include "vtls/vtls.h"
+#include "strdup.h"
#define BUILDING_CURL_NTLM_MSGS_C
#include "vauth/vauth.h"
@@ -184,11 +185,10 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
}
free(ntlm->target_info); /* replace any previous data */
- ntlm->target_info = malloc(target_info_len);
+ ntlm->target_info = Curl_memdup(&type2[target_info_offset],
+ target_info_len);
if(!ntlm->target_info)
return CURLE_OUT_OF_MEMORY;
-
- memcpy(ntlm->target_info, &type2[target_info_offset], target_info_len);
}
}
diff --git a/libs/libcurl/src/vauth/ntlm_sspi.c b/libs/libcurl/src/vauth/ntlm_sspi.c
index b2cf248181..f8c3558603 100644
--- a/libs/libcurl/src/vauth/ntlm_sspi.c
+++ b/libs/libcurl/src/vauth/ntlm_sspi.c
@@ -34,6 +34,7 @@
#include "warnless.h"
#include "curl_multibyte.h"
#include "sendf.h"
+#include "strdup.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -213,11 +214,10 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
}
/* Store the challenge for later use */
- ntlm->input_token = malloc(Curl_bufref_len(type2) + 1);
+ ntlm->input_token = Curl_memdup0((const char *)Curl_bufref_ptr(type2),
+ Curl_bufref_len(type2));
if(!ntlm->input_token)
return CURLE_OUT_OF_MEMORY;
- memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2));
- ntlm->input_token[Curl_bufref_len(type2)] = '\0';
ntlm->input_token_len = Curl_bufref_len(type2);
return CURLE_OK;
@@ -314,7 +314,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
&type_3_desc,
&attrs, &expiry);
if(status != SEC_E_OK) {
- infof(data, "NTLM handshake failure (type-3 message): Status=%x",
+ infof(data, "NTLM handshake failure (type-3 message): Status=%lx",
status);
if(status == SEC_E_INSUFFICIENT_MEMORY)
diff --git a/libs/libcurl/src/version.c b/libs/libcurl/src/version.c
index 86c8aff13b..b98c831177 100644
--- a/libs/libcurl/src/version.c
+++ b/libs/libcurl/src/version.c
@@ -211,8 +211,12 @@ char *curl_version(void)
#endif
#ifdef USE_LIBPSL
- msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version());
- src[i++] = psl_version;
+ {
+ int num = psl_check_version_number(0);
+ msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d",
+ num >> 16, (num >> 8) & 0xff, num & 0xff);
+ src[i++] = psl_version;
+ }
#endif
#ifdef USE_SSH
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c
index bf8220470c..9835ccc5e1 100644
--- a/libs/libcurl/src/vquic/curl_msh3.c
+++ b/libs/libcurl/src/vquic/curl_msh3.c
@@ -204,8 +204,8 @@ static void drain_stream_from_other_thread(struct Curl_easy *data,
bits = CURL_CSELECT_IN;
if(stream && !stream->upload_done)
bits |= CURL_CSELECT_OUT;
- if(data->state.dselect_bits != bits) {
- data->state.dselect_bits = bits;
+ if(data->state.select_bits != bits) {
+ data->state.select_bits = bits;
/* cannot expire from other thread */
}
}
@@ -220,8 +220,8 @@ static void drain_stream(struct Curl_cfilter *cf,
bits = CURL_CSELECT_IN;
if(stream && !stream->upload_done)
bits |= CURL_CSELECT_OUT;
- if(data->state.dselect_bits != bits) {
- data->state.dselect_bits = bits;
+ if(data->state.select_bits != bits) {
+ data->state.select_bits = bits;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c
index 9584065f5a..d004c9d27f 100644
--- a/libs/libcurl/src/vquic/curl_ngtcp2.c
+++ b/libs/libcurl/src/vquic/curl_ngtcp2.c
@@ -41,7 +41,6 @@
#include "vtls/gtls.h"
#elif defined(USE_WOLFSSL)
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
#endif
#include "urldata.h"
@@ -61,6 +60,7 @@
#include "inet_pton.h"
#include "vquic.h"
#include "vquic_int.h"
+#include "vquic-tls.h"
#include "vtls/keylog.h"
#include "vtls/vtls.h"
#include "curl_ngtcp2.h"
@@ -73,9 +73,6 @@
#include "memdebug.h"
-#define H3_ALPN_H3_29 "\x5h3-29"
-#define H3_ALPN_H3 "\x2h3"
-
#define QUIC_MAX_STREAMS (256*1024)
#define QUIC_MAX_DATA (1*1024*1024)
#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
@@ -101,25 +98,6 @@
(H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-
/*
* Store ngtcp2 version info in this buffer.
*/
@@ -134,6 +112,7 @@ void Curl_ngtcp2_ver(char *p, size_t len)
struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
+ struct quic_tls_ctx tls;
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
@@ -143,30 +122,16 @@ struct cf_ngtcp2_ctx {
ngtcp2_transport_params transport_params;
ngtcp2_ccerr last_error;
ngtcp2_crypto_conn_ref conn_ref;
-#ifdef USE_OPENSSL
- SSL_CTX *sslctx;
- SSL *ssl;
-#elif defined(USE_GNUTLS)
- struct gtls_instance *gtls;
-#elif defined(USE_WOLFSSL)
- WOLFSSL_CTX *sslctx;
- WOLFSSL *ssl;
-#endif
struct cf_call_data call_data;
nghttp3_conn *h3conn;
nghttp3_settings h3settings;
struct curltime started_at; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct curltime first_byte_at; /* when first byte was recvd */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
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 */
@@ -292,8 +257,8 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
bits = CURL_CSELECT_IN;
if(stream && stream->upload_left && !stream->send_closed)
bits |= CURL_CSELECT_OUT;
- if(data->state.dselect_bits != bits) {
- data->state.dselect_bits = bits;
+ if(data->state.select_bits != bits) {
+ data->state.select_bits = bits;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
@@ -413,388 +378,8 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
}
}
-#ifdef USE_OPENSSL
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
- const gnutls_datum_t *secret)
-{
- gnutls_datum_t crandom;
- gnutls_datum_t srandom;
-
- gnutls_session_get_random(session, &crandom, &srandom);
- if(crandom.size != 32) {
- return -1;
- }
-
- Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
- return 0;
-}
-#elif defined(USE_WOLFSSL)
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#endif
-#endif
-
static int init_ngh3_conn(struct Curl_cfilter *cf);
-#ifdef USE_OPENSSL
-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 ssl_primary_config *conn_config;
- CURLcode result = CURLE_FAILED_INIT;
-
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
- if(!ssl_ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config) {
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
-#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
- if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
- goto out;
- }
-#else
- if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
- goto out;
- }
-#endif
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
- {
- const char *curves = conn_config->curves ?
- conn_config->curves : QUIC_GROUPS;
- if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) {
- failf(data, "failed setting curves list for QUIC: '%s'", curves);
- return CURLE_SSL_CIPHER;
- }
- }
-
-#ifndef OPENSSL_IS_BORINGSSL
- {
- const char *ciphers13 = conn_config->cipher_list13 ?
- conn_config->cipher_list13 : QUIC_CIPHERS;
- if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) {
- failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
- return CURLE_SSL_CIPHER;
- }
- infof(data, "QUIC cipher selection: %s", ciphers13);
- }
-#endif
-
- /* 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);
- }
-
- /* 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
- * SSL_get_verify_result() below. */
- SSL_CTX_set_verify(ssl_ctx, conn_config->verifypeer ?
- SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-
- /* 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);
- Curl_set_in_callback(data, false);
- if(result) {
- failf(data, "error signaled by ssl ctx callback");
- goto out;
- }
- }
- result = CURLE_OK;
-
-out:
- *pssl_ctx = result? NULL : ssl_ctx;
- if(result && ssl_ctx)
- SSL_CTX_free(ssl_ctx);
- return result;
-}
-
-static CURLcode quic_set_client_cert(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- SSL_CTX *ssl_ctx = ctx->sslctx;
- const struct ssl_config_data *ssl_config;
-
- ssl_config = Curl_ssl_cf_get_config(cf, data);
- DEBUGASSERT(ssl_config);
-
- if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
- || ssl_config->cert_type) {
- return Curl_ossl_set_client_cert(
- data, ssl_ctx, ssl_config->primary.clientcert,
- ssl_config->primary.cert_blob, ssl_config->cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd);
- }
-
- return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
-
- DEBUGASSERT(!ctx->ssl);
- ctx->ssl = SSL_new(ctx->sslctx);
-
- SSL_set_app_data(ctx->ssl, &ctx->conn_ref);
- SSL_set_connect_state(ctx->ssl);
- SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- if(ctx->peer.sni) {
- if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) {
- failf(data, "Failed set SNI");
- SSL_free(ctx->ssl);
- ctx->ssl = NULL;
- return CURLE_QUIC_CONNECT_ERROR;
- }
- }
- return CURLE_OK;
-}
-#elif defined(USE_GNUTLS)
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct ssl_primary_config *conn_config;
- CURLcode result;
- gnutls_datum_t alpn[2];
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- long * const pverifyresult = &data->set.ssl.certverifyresult;
- int rc;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
-
- DEBUGASSERT(ctx->gtls == NULL);
- ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
- if(!ctx->gtls)
- return CURLE_OUT_OF_MEMORY;
-
- result = gtls_client_init(data, conn_config, &data->set.ssl,
- &ctx->peer, ctx->gtls, pverifyresult);
- if(result)
- return result;
-
- gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
-
- if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
- 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) {
- CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
- gnutls_strerror(rc));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
- }
-
- /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
- alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
- alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
- alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
- alpn[1].size = sizeof(H3_ALPN_H3) - 2;
-
- gnutls_alpn_set_protocols(ctx->gtls->session,
- alpn, 2, GNUTLS_ALPN_MANDATORY);
- return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
-
-static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx,
- struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- CURLcode result = CURLE_FAILED_INIT;
- struct ssl_primary_config *conn_config;
- WOLFSSL_CTX *ssl_ctx = NULL;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config) {
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
- ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
- if(!ssl_ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
-
- if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn_config->cipher_list13 ?
- conn_config->cipher_list13 :
- QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
- goto out;
- }
-
- if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn_config->curves ?
- conn_config->curves :
- (char *)QUIC_GROUPS) != 1) {
- failf(data, "wolfSSL failed to set curves");
- goto out;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-#else
- failf(data, "wolfSSL was built without keylog callback");
- goto out;
-#endif
- }
-
- if(conn_config->verifypeer) {
- const char * const ssl_cafile = conn_config->CAfile;
- const char * const ssl_capath = conn_config->CApath;
-
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- if(ssl_cafile || ssl_capath) {
- /* tell wolfSSL where to find CA certificates that are used to verify
- the server's certificate. */
- int rc =
- wolfSSL_CTX_load_verify_locations_ex(ssl_ctx, ssl_cafile, ssl_capath,
- WOLFSSL_LOAD_FLAG_IGNORE_ERR);
- if(SSL_SUCCESS != rc) {
- /* 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");
- goto out;
- }
- 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 wolfssl's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- else {
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
- }
-
- /* give application a chance to interfere with SSL set up. */
- if(data->set.ssl.fsslctx) {
- Curl_set_in_callback(data, true);
- result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
- data->set.ssl.fsslctxp);
- Curl_set_in_callback(data, false);
- if(result) {
- failf(data, "error signaled by ssl ctx callback");
- goto out;
- }
- }
- result = CURLE_OK;
-
-out:
- *pssl_ctx = result? NULL : ssl_ctx;
- if(result && ssl_ctx)
- SSL_CTX_free(ssl_ctx);
- return result;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = cf->conn->host.name;
-
- (void)data;
- DEBUGASSERT(!ctx->ssl);
- ctx->ssl = wolfSSL_new(ctx->sslctx);
-
- wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref);
- wolfSSL_set_connect_state(ctx->ssl);
- wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
- hostname, (unsigned short)strlen(hostname));
-
- return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
{
(void)user_data;
@@ -1157,18 +742,22 @@ static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf,
struct easy_pollset *ps)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- bool want_recv = CURL_WANT_RECV(data);
- bool want_send = CURL_WANT_SEND(data);
+ bool want_recv, want_send;
- if(ctx->qconn && (want_recv || want_send)) {
+ if(!ctx->qconn)
+ return;
+
+ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+ if(want_recv || want_send) {
struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
struct cf_call_data save;
bool c_exhaust, s_exhaust;
CF_DATA_SAVE(save, cf, data);
- c_exhaust = !ngtcp2_conn_get_cwnd_left(ctx->qconn) ||
- !ngtcp2_conn_get_max_data_left(ctx->qconn);
- s_exhaust = stream && stream->id >= 0 && stream->quic_flow_blocked;
+ c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) ||
+ !ngtcp2_conn_get_max_data_left(ctx->qconn));
+ s_exhaust = want_send && stream && stream->id >= 0 &&
+ stream->quic_flow_blocked;
want_recv = (want_recv || c_exhaust || s_exhaust);
want_send = (!s_exhaust && want_send) ||
!Curl_bufq_is_empty(&ctx->q.sendbuf);
@@ -1894,6 +1483,8 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
sent = (ssize_t)len;
goto out;
}
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> stream closed", stream->id, len);
*err = CURLE_HTTP3;
sent = -1;
goto out;
@@ -1944,49 +1535,12 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct ssl_primary_config *conn_config;
- CURLcode result = CURLE_OK;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
- if(conn_config->verifyhost) {
-#ifdef USE_OPENSSL
- X509 *server_cert;
- server_cert = SSL_get1_peer_certificate(ctx->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
-#elif defined(USE_GNUTLS)
- result = Curl_gtls_verifyserver(data, ctx->gtls->session,
- conn_config, &data->set.ssl, &ctx->peer,
- data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- if(!ctx->peer.sni ||
- wolfSSL_check_domain_name(ctx->ssl, ctx->peer.sni) == SSL_FAILURE)
- return CURLE_PEER_FAILED_VERIFICATION;
-#endif
- infof(data, "Verified certificate just fine");
- }
- else
- infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, ctx->ssl);
-#endif
- return result;
+ return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
@@ -2050,14 +1604,9 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
pktx_update_time(pktx, cf);
}
-#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
+ result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
+ if(result)
+ return result;
for(i = 0; i < pkts_max; i += pkts_chunk) {
pktx->pkt_count = 0;
@@ -2382,25 +1931,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
if(ctx->qlogfd != -1) {
close(ctx->qlogfd);
}
-#ifdef USE_OPENSSL
- if(ctx->ssl)
- SSL_free(ctx->ssl);
- if(ctx->sslctx)
- SSL_CTX_free(ctx->sslctx);
-#elif defined(USE_GNUTLS)
- if(ctx->gtls) {
- if(ctx->gtls->cred)
- gnutls_certificate_free_credentials(ctx->gtls->cred);
- if(ctx->gtls->session)
- gnutls_deinit(ctx->gtls->session);
- free(ctx->gtls);
- }
-#elif defined(USE_WOLFSSL)
- if(ctx->ssl)
- wolfSSL_free(ctx->ssl);
- if(ctx->sslctx)
- wolfSSL_CTX_free(ctx->sslctx);
-#endif
+ Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q);
if(ctx->h3conn)
nghttp3_conn_del(ctx->h3conn);
@@ -2459,6 +1990,37 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)save;
}
+static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+#ifdef USE_OPENSSL
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
+ return CURLE_FAILED_INIT;
+ }
+#else
+ if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
+ return CURLE_FAILED_INIT;
+ }
+#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+#elif defined(USE_GNUTLS)
+ if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+ failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed");
+ return CURLE_FAILED_INIT;
+ }
+#elif defined(USE_WOLFSSL)
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
+ return CURLE_FAILED_INIT;
+ }
+#endif
+ return CURLE_OK;
+}
+
/*
* Might be called twice for happy eyeballs.
*/
@@ -2483,21 +2045,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
if(result)
return result;
-#ifdef USE_OPENSSL
- result = quic_ssl_ctx(&ctx->sslctx, cf, data);
- if(result)
- return result;
-
- result = quic_set_client_cert(cf, data);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- result = quic_ssl_ctx(&ctx->sslctx, cf, data);
- if(result)
- return result;
-#endif
-
- result = quic_init_ssl(cf, data);
+#define H3_ALPN "\x2h3\x5h3-29"
+ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+ H3_ALPN, sizeof(H3_ALPN) - 1,
+ tls_ctx_setup, &ctx->conn_ref);
if(result)
return result;
@@ -2544,9 +2095,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
return CURLE_QUIC_CONNECT_ERROR;
#ifdef USE_GNUTLS
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session);
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session);
#else
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl);
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl);
#endif
ngtcp2_ccerr_default(&ctx->last_error);
@@ -2677,8 +2228,8 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
- if(ctx->got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ if(ctx->q.got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
}
else
@@ -2686,8 +2237,8 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
return CURLE_OK;
case CF_QUERY_TIMER_CONNECT: {
struct curltime *when = pres2;
- if(ctx->got_first_byte)
- *when = ctx->first_byte_at;
+ if(ctx->q.got_first_byte)
+ *when = ctx->q.first_byte_at;
return CURLE_OK;
}
case CF_QUERY_TIMER_APPCONNECT: {
diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c
new file mode 100644
index 0000000000..82fda51971
--- /dev/null
+++ b/libs/libcurl/src/vquic/curl_osslq.c
@@ -0,0 +1,2237 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <nghttp3/nghttp3.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "dynbuf.h"
+#include "http1.h"
+#include "select.h"
+#include "inet_pton.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "vquic-tls.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "vtls/openssl.h"
+#include "curl_osslq.h"
+
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* A stream window is the maximum amount we need to buffer for
+ * each active transfer. We use HTTP/3 flow control and only ACK
+ * when we take things out of the buffer.
+ * Chunk size is large enough to take a full DATA frame */
+#define H3_STREAM_WINDOW_SIZE (128 * 1024)
+#define H3_STREAM_CHUNK_SIZE (16 * 1024)
+/* The pool keeps spares around and half of a full stream windows
+ * seems good. More does not seem to improve performance.
+ * The benefit of the pool is that stream buffer to not keep
+ * spares. So memory consumption goes down when streams run empty,
+ * have a large upload done, etc. */
+#define H3_STREAM_POOL_SPARES \
+ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
+/* Receive and Send max number of chunks just follows from the
+ * chunk size and window size */
+#define H3_STREAM_RECV_CHUNKS \
+ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
+#define H3_STREAM_SEND_CHUNKS \
+ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+typedef uint32_t sslerr_t;
+#else
+typedef unsigned long sslerr_t;
+#endif
+
+
+/* How to access `call_data` from a cf_osslq filter */
+#undef CF_CTX_CALL_DATA
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+static const char *SSL_ERROR_to_str(int err)
+{
+ switch(err) {
+ case SSL_ERROR_NONE:
+ return "SSL_ERROR_NONE";
+ case SSL_ERROR_SSL:
+ return "SSL_ERROR_SSL";
+ case SSL_ERROR_WANT_READ:
+ return "SSL_ERROR_WANT_READ";
+ case SSL_ERROR_WANT_WRITE:
+ return "SSL_ERROR_WANT_WRITE";
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return "SSL_ERROR_WANT_X509_LOOKUP";
+ case SSL_ERROR_SYSCALL:
+ return "SSL_ERROR_SYSCALL";
+ case SSL_ERROR_ZERO_RETURN:
+ return "SSL_ERROR_ZERO_RETURN";
+ case SSL_ERROR_WANT_CONNECT:
+ return "SSL_ERROR_WANT_CONNECT";
+ case SSL_ERROR_WANT_ACCEPT:
+ return "SSL_ERROR_WANT_ACCEPT";
+#if defined(SSL_ERROR_WANT_ASYNC)
+ case SSL_ERROR_WANT_ASYNC:
+ return "SSL_ERROR_WANT_ASYNC";
+#endif
+#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+ case SSL_ERROR_WANT_ASYNC_JOB:
+ return "SSL_ERROR_WANT_ASYNC_JOB";
+#endif
+#if defined(SSL_ERROR_WANT_EARLY)
+ case SSL_ERROR_WANT_EARLY:
+ return "SSL_ERROR_WANT_EARLY";
+#endif
+ default:
+ return "SSL_ERROR unknown";
+ }
+}
+
+/* Return error string for last OpenSSL error */
+static char *ossl_strerror(unsigned long error, char *buf, size_t size)
+{
+ DEBUGASSERT(size);
+ *buf = '\0';
+
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ ERR_error_string_n((uint32_t)error, buf, size);
+#else
+ ERR_error_string_n(error, buf, size);
+#endif
+
+ if(!*buf) {
+ const char *msg = error ? "Unknown error" : "No error";
+ if(strlen(msg) < size)
+ strcpy(buf, msg);
+ }
+
+ return buf;
+}
+
+static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
+ const struct Curl_sockaddr_ex *addr)
+{
+ BIO_ADDR *ba;
+ CURLcode result = CURLE_FAILED_INIT;
+
+ ba = BIO_ADDR_new();
+ if(!ba) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ switch(addr->family) {
+ case AF_INET: {
+ struct sockaddr_in * const sin =
+ (struct sockaddr_in * const)(void *)&addr->sa_addr;
+ if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
+ sizeof(sin->sin_addr), sin->sin_port)) {
+ goto out;
+ }
+ result = CURLE_OK;
+ break;
+ }
+#ifdef ENABLE_IPV6
+ case AF_INET6: {
+ struct sockaddr_in6 * const sin =
+ (struct sockaddr_in6 * const)(void *)&addr->sa_addr;
+ if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
+ sizeof(sin->sin6_addr), sin->sin6_port)) {
+ }
+ result = CURLE_OK;
+ break;
+ }
+#endif /* ENABLE_IPV6 */
+ default:
+ /* sunsupported */
+ DEBUGASSERT(0);
+ break;
+ }
+
+out:
+ if(result && ba) {
+ BIO_ADDR_free(ba);
+ ba = NULL;
+ }
+ *pbio_addr = ba;
+ return result;
+}
+
+/* QUIC stream (not necessarily H3) */
+struct cf_osslq_stream {
+ int64_t id;
+ SSL *ssl;
+ struct bufq recvbuf; /* QUIC war data recv buffer */
+ BIT(recvd_eos);
+ BIT(closed);
+ BIT(reset);
+ BIT(send_blocked);
+};
+
+static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
+ SSL *conn,
+ uint64_t flags,
+ struct bufc_pool *bufcp,
+ void *user_data)
+{
+ DEBUGASSERT(!s->ssl);
+ Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
+ s->ssl = SSL_new_stream(conn, flags);
+ if(!s->ssl) {
+ return CURLE_FAILED_INIT;
+ }
+ s->id = SSL_get_stream_id(s->ssl);
+ SSL_set_app_data(s->ssl, user_data);
+ return CURLE_OK;
+}
+
+static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
+{
+ if(s->ssl) {
+ SSL_set_app_data(s->ssl, NULL);
+ SSL_free(s->ssl);
+ }
+ Curl_bufq_free(&s->recvbuf);
+ memset(s, 0, sizeof(*s));
+}
+
+static void cf_osslq_stream_close(struct cf_osslq_stream *s)
+{
+ if(s->ssl) {
+ SSL_free(s->ssl);
+ s->ssl = NULL;
+ }
+}
+
+struct cf_osslq_h3conn {
+ nghttp3_conn *conn;
+ nghttp3_settings settings;
+ struct cf_osslq_stream s_ctrl;
+ struct cf_osslq_stream s_qpack_enc;
+ struct cf_osslq_stream s_qpack_dec;
+ struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
+ size_t remote_ctrl_n; /* number of peer streams opened */
+};
+
+static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
+{
+ size_t i;
+
+ if(h3->conn)
+ nghttp3_conn_del(h3->conn);
+ cf_osslq_stream_cleanup(&h3->s_ctrl);
+ cf_osslq_stream_cleanup(&h3->s_qpack_enc);
+ cf_osslq_stream_cleanup(&h3->s_qpack_dec);
+ for(i = 0; i < h3->remote_ctrl_n; ++i) {
+ cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
+ }
+}
+
+struct cf_osslq_ctx {
+ struct cf_quic_ctx q;
+ struct ssl_peer peer;
+ struct quic_tls_ctx tls;
+ struct cf_call_data call_data;
+ struct cf_osslq_h3conn h3;
+ struct curltime started_at; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ struct curltime reconnect_at; /* time the next attempt should start */
+ struct bufc_pool stream_bufcp; /* chunk pool for streams */
+ size_t max_stream_window; /* max flow window for one stream */
+ uint64_t max_idle_ms; /* max idle time for QUIC connection */
+ BIT(got_first_byte); /* if first byte was received */
+#ifdef USE_OPENSSL
+ BIT(x509_store_setup); /* if x509 store has been set up */
+ BIT(protocol_shutdown); /* QUIC connection is shut down */
+#endif
+};
+
+static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
+{
+ struct cf_call_data save = ctx->call_data;
+
+ cf_osslq_h3conn_cleanup(&ctx->h3);
+ Curl_vquic_tls_cleanup(&ctx->tls);
+ vquic_ctx_free(&ctx->q);
+ Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_ssl_peer_cleanup(&ctx->peer);
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->call_data = save;
+}
+
+static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ if(ctx && ctx->tls.ssl) {
+ /* TODO: send connection close */
+ CURL_TRC_CF(data, cf, "cf_osslq_close()");
+ cf_osslq_ctx_clear(ctx);
+ }
+
+ cf->connected = FALSE;
+ CF_DATA_RESTORE(cf, save);
+}
+
+static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ CURL_TRC_CF(data, cf, "destroy");
+ if(ctx) {
+ CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
+ cf_osslq_ctx_clear(ctx);
+ free(ctx);
+ }
+ cf->ctx = NULL;
+ /* No CF_DATA_RESTORE(cf, save) possible */
+ (void)save;
+}
+
+static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
+ SSL *stream_ssl,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ int64_t stream_id = SSL_get_stream_id(stream_ssl);
+
+ if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
+ /* rejected, we are full */
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream",
+ stream_id);
+ SSL_free(stream_ssl);
+ return CURLE_FAILED_INIT;
+ }
+ switch(SSL_get_stream_type(stream_ssl)) {
+ case SSL_STREAM_TYPE_READ: {
+ struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
+ nstream->id = stream_id;
+ nstream->ssl = stream_ssl;
+ Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted new remote uni stream",
+ stream_id);
+ break;
+ }
+ default:
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read"
+ " stream", stream_id);
+ SSL_free(stream_ssl);
+ return CURLE_FAILED_INIT;
+ }
+ return CURLE_OK;
+
+}
+
+static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int detail, CURLcode def_result)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = def_result;
+ sslerr_t errdetail;
+ char ebuf[256] = "unknown";
+ const char *err_descr = ebuf;
+ long lerr;
+ int lib;
+ int reason;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+
+ errdetail = ERR_get_error();
+ lib = ERR_GET_LIB(errdetail);
+ reason = ERR_GET_REASON(errdetail);
+
+ if((lib == ERR_LIB_SSL) &&
+ ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
+ (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+
+ lerr = SSL_get_verify_result(ctx->tls.ssl);
+ if(lerr != X509_V_OK) {
+ ssl_config->certverifyresult = lerr;
+ msnprintf(ebuf, sizeof(ebuf),
+ "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
+ }
+ else
+ err_descr = "SSL certificate verification failed";
+ }
+#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) &&
+ (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
+ /* If client certificate is required, communicate the
+ error to client */
+ result = CURLE_SSL_CLIENTCERT;
+ ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+ }
+#endif
+ else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
+ ctx->protocol_shutdown = TRUE;
+ err_descr = "QUIC connectin has been shut down";
+ result = def_result;
+ }
+ else {
+ result = def_result;
+ ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+ }
+
+ /* detail is already set to the SSL error above */
+
+ /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
+ * the SO_ERROR is also lost.
+ */
+ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
+ char extramsg[80]="";
+ int sockerr = SOCKERRNO;
+ const char *r_ip = NULL;
+ int r_port = 0;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ if(sockerr && detail == SSL_ERROR_SYSCALL)
+ Curl_strerror(sockerr, extramsg, sizeof(extramsg));
+ failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
+ extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
+ ctx->peer.dispname, r_port, r_ip);
+ }
+ else {
+ /* Could be a CERT problem */
+ failf(data, "%s", err_descr);
+ }
+ return result;
+}
+
+static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
+}
+
+/**
+ * All about the H3 internals of a stream
+ */
+struct h3_stream_ctx {
+ struct cf_osslq_stream s;
+ 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 */
+ curl_off_t download_recvd; /* number of response DATA bytes received */
+ int status_code; /* HTTP status code */
+ bool resp_hds_complete; /* we have a complete, final response */
+ bool closed; /* TRUE on stream close */
+ bool reset; /* TRUE on stream reset */
+ bool send_closed; /* stream is local closed */
+ BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
+};
+
+#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)->s.id : -2)
+
+static CURLcode h3_data_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+ if(!data || !data->req.p.http) {
+ failf(data, "initialization failure, transfer not http initialized");
+ return CURLE_FAILED_INIT;
+ }
+
+ if(stream)
+ return CURLE_OK;
+
+ stream = calloc(1, sizeof(*stream));
+ if(!stream)
+ return CURLE_OUT_OF_MEMORY;
+
+ stream->s.id = -1;
+ /* on send, we control how much we put into the buffer */
+ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
+ H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
+ stream->sendbuf_len_in_flight = 0;
+ /* on recv, we need a flexible buffer limit since we also write
+ * headers to it that are not counted against the nghttp3 flow limits. */
+ 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;
+ return CURLE_OK;
+}
+
+static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+ (void)cf;
+ if(stream) {
+ CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id);
+ if(ctx->h3.conn && !stream->closed) {
+ nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
+ nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
+ NGHTTP3_H3_REQUEST_CANCELLED);
+ nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
+ stream->closed = TRUE;
+ }
+
+ cf_osslq_stream_cleanup(&stream->s);
+ Curl_bufq_free(&stream->sendbuf);
+ Curl_bufq_free(&stream->recvbuf);
+ Curl_h1_req_parse_free(&stream->h1);
+ free(stream);
+ H3_STREAM_LCTX(data) = NULL;
+ }
+}
+
+static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int64_t stream_id)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ struct Curl_easy *sdata;
+
+ if(stream && stream->s.id == stream_id) {
+ return &stream->s;
+ }
+ else if(ctx->h3.s_ctrl.id == stream_id) {
+ return &ctx->h3.s_ctrl;
+ }
+ else if(ctx->h3.s_qpack_enc.id == stream_id) {
+ return &ctx->h3.s_qpack_enc;
+ }
+ else if(ctx->h3.s_qpack_dec.id == stream_id) {
+ return &ctx->h3.s_qpack_dec;
+ }
+ else {
+ DEBUGASSERT(data->multi);
+ for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) {
+ stream = H3_STREAM_CTX(sdata);
+ return stream? &stream->s : NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void h3_drain_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ unsigned char bits;
+
+ (void)cf;
+ bits = CURL_CSELECT_IN;
+ if(stream && stream->upload_left && !stream->send_closed)
+ bits |= CURL_CSELECT_OUT;
+ if(data->state.select_bits != bits) {
+ data->state.select_bits = bits;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+}
+
+static CURLcode h3_data_pause(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool pause)
+{
+ if(!pause) {
+ /* unpaused. make it run again right away */
+ h3_drain_stream(cf, data);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ return CURLE_OK;
+}
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ (void)conn;
+ (void)stream_id;
+
+ /* we might be called by nghttp3 after we already cleaned up */
+ if(!stream)
+ return 0;
+
+ stream->closed = TRUE;
+ stream->error3 = app_error_code;
+ if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
+ stream->reset = TRUE;
+ stream->send_closed = TRUE;
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
+ stream->s.id, stream->error3);
+ }
+ else {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id);
+ }
+ h3_drain_stream(cf, data);
+ return 0;
+}
+
+/*
+ * write_resp_raw() copies response data in raw format to the `data`'s
+ * receive buffer. If not enough space is available, it appends to the
+ * `data`'s overflow buffer.
+ */
+static CURLcode write_resp_raw(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem, size_t memlen,
+ bool flow)
+{
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ CURLcode result = CURLE_OK;
+ ssize_t nwritten;
+
+ (void)cf;
+ if(!stream) {
+ return CURLE_RECV_ERROR;
+ }
+ nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
+ if(nwritten < 0) {
+ return result;
+ }
+
+ if(!flow)
+ stream->recv_buf_nonflow += (size_t)nwritten;
+
+ if((size_t)nwritten < memlen) {
+ /* This MUST not happen. Our recbuf is dimensioned to hold the
+ * full max_stream_window and then some for this very reason. */
+ DEBUGASSERT(0);
+ return CURLE_RECV_ERROR;
+ }
+ return result;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct 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);
+ if(result) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
+ stream->s.id, buflen, result);
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ stream->download_recvd += (curl_off_t)buflen;
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%zd",
+ stream->s.id, buflen, stream->download_recvd);
+ h3_drain_stream(cf, data);
+ return 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+ (void)conn;
+ (void)stream_id;
+ if(stream)
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes",
+ stream->s.id, consumed);
+ return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+ nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)flags;
+ (void)cf;
+
+ /* we might have cleaned up this transfer already */
+ if(!stream)
+ return 0;
+
+ if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
+ char line[14]; /* status line is always 13 characters long */
+ size_t ncopy;
+
+ result = Curl_http_decode_status(&stream->status_code,
+ (const char *)h3val.base, h3val.len);
+ if(result)
+ return -1;
+ ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
+ stream->status_code);
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
+ result = write_resp_raw(cf, data, line, ncopy, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+ else {
+ /* store as an HTTP1-style header */
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
+ stream_id, (int)h3name.len, h3name.base,
+ (int)h3val.len, h3val.base);
+ result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, ": ", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)fin;
+ (void)cf;
+
+ if(!stream)
+ return 0;
+ /* add a CRLF only if we've received some headers */
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+
+ 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;
+ }
+ h3_drain_stream(cf, data);
+ return 0;
+}
+
+static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ (void)conn;
+ (void)app_error_code;
+
+ if(!stream || !stream->s.ssl)
+ return 0;
+
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id);
+ cf_osslq_stream_close(&stream->s);
+ return 0;
+}
+
+static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ int rv;
+ (void)conn;
+
+ if(stream && stream->s.ssl) {
+ SSL_STREAM_RESET_ARGS args = {0};
+ args.quic_error_code = app_error_code;
+ rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
+ if(!rv) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ return 0;
+}
+
+static nghttp3_ssize
+cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ ssize_t nwritten = 0;
+ size_t nvecs = 0;
+ (void)cf;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)veccnt;
+
+ if(!stream)
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ /* nghttp3 keeps references to the sendbuf data until it is ACKed
+ * by the server (see `cb_h3_acked_req_body()` for updates).
+ * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
+ * that we have already passed to nghttp3, but which have not been
+ * ACKed yet.
+ * Any amount beyond `sendbuf_len_in_flight` we need still to pass
+ * to nghttp3. Do that now, if we can. */
+ if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
+ nvecs = 0;
+ while(nvecs < veccnt &&
+ Curl_bufq_peek_at(&stream->sendbuf,
+ stream->sendbuf_len_in_flight,
+ (const unsigned char **)&vec[nvecs].base,
+ &vec[nvecs].len)) {
+ stream->sendbuf_len_in_flight += vec[nvecs].len;
+ nwritten += vec[nvecs].len;
+ ++nvecs;
+ }
+ DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
+ }
+
+ if(nwritten > 0 && stream->upload_left != -1)
+ stream->upload_left -= nwritten;
+
+ /* When we stopped sending and everything in `sendbuf` is "in flight",
+ * we are at the end of the request body. */
+ if(stream->upload_left == 0) {
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ stream->send_closed = TRUE;
+ }
+ else if(!nwritten) {
+ /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
+ stream->s.id);
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
+ "%d vecs%s with %zu (buffered=%zu, left=%"
+ CURL_FORMAT_CURL_OFF_T ")",
+ stream->s.id, (int)nvecs,
+ *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ nwritten, Curl_bufq_len(&stream->sendbuf),
+ stream->upload_left);
+ return (nghttp3_ssize)nvecs;
+}
+
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ size_t skiplen;
+
+ (void)cf;
+ if(!stream)
+ return 0;
+ /* The server acknowledged `datalen` of bytes from our request body.
+ * This is a delta. We have kept this data in `sendbuf` for
+ * re-transmissions and can free it now. */
+ if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
+ skiplen = stream->sendbuf_len_in_flight;
+ else
+ skiplen = (size_t)datalen;
+ Curl_bufq_skip(&stream->sendbuf, skiplen);
+ stream->sendbuf_len_in_flight -= skiplen;
+
+ /* Everything ACKed, we resume upload processing */
+ if(!stream->sendbuf_len_in_flight) {
+ int rv = nghttp3_conn_resume_stream(conn, stream_id);
+ if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ return 0;
+}
+
+static nghttp3_callbacks ngh3_callbacks = {
+ cb_h3_acked_stream_data,
+ cb_h3_stream_close,
+ cb_h3_recv_data,
+ cb_h3_deferred_consume,
+ NULL, /* begin_headers */
+ cb_h3_recv_header,
+ cb_h3_end_headers,
+ NULL, /* begin_trailers */
+ cb_h3_recv_header,
+ NULL, /* end_trailers */
+ cb_h3_stop_sending,
+ NULL, /* end_stream */
+ cb_h3_reset_stream,
+ NULL, /* shutdown */
+ NULL /* recv_settings */
+};
+
+static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
+ void *user_data)
+{
+ struct cf_osslq_h3conn *h3 = &ctx->h3;
+ CURLcode result;
+ int rc;
+
+ nghttp3_settings_default(&h3->settings);
+ rc = nghttp3_conn_client_new(&h3->conn,
+ &ngh3_callbacks,
+ &h3->settings,
+ nghttp3_mem_default(),
+ user_data);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ result = cf_osslq_stream_open(&h3->s_ctrl, conn,
+ SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+ &ctx->stream_bufcp, NULL);
+ if(result) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto out;
+ }
+ result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
+ SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+ &ctx->stream_bufcp, NULL);
+ if(result) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto out;
+ }
+ result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
+ SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+ &ctx->stream_bufcp, NULL);
+ if(result) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto out;
+ }
+
+ rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto out;
+ }
+ rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
+ h3->s_qpack_dec.id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto out;
+ }
+
+ result = CURLE_OK;
+out:
+ return result;
+}
+
+static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result;
+ int rv;
+ const struct Curl_sockaddr_ex *peer_addr = NULL;
+ int peer_port;
+ BIO *bio = NULL;
+ BIO_ADDR *baddr = NULL;
+
+ Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
+ H3_STREAM_POOL_SPARES);
+ result = Curl_ssl_peer_init(&ctx->peer, cf);
+ if(result)
+ goto out;
+
+#define H3_ALPN "\x2h3"
+ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+ H3_ALPN, sizeof(H3_ALPN) - 1,
+ NULL, NULL);
+ if(result)
+ goto out;
+
+ result = vquic_ctx_init(&ctx->q);
+ if(result)
+ goto out;
+
+ result = CURLE_QUIC_CONNECT_ERROR;
+ Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+ &peer_addr, NULL, &peer_port, NULL, NULL);
+ if(!peer_addr)
+ goto out;
+
+ ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
+ rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
+ &ctx->q.local_addrlen);
+ if(rv == -1)
+ goto out;
+
+ result = make_bio_addr(&baddr, peer_addr);
+ if(result) {
+ failf(data, "error creating BIO_ADDR from sockaddr");
+ goto out;
+ }
+
+ bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
+ if(!bio) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
+ failf(data, "failed to set the initial peer address");
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+ if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
+ failf(data, "failed to turn off blocking mode");
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+
+ SSL_set_bio(ctx->tls.ssl, bio, bio);
+ bio = NULL;
+ SSL_set_connect_state(ctx->tls.ssl);
+ SSL_set_incoming_stream_policy(ctx->tls.ssl,
+ SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
+ /* setup the H3 things on top of the QUIC connection */
+ result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
+
+out:
+ if(bio)
+ BIO_free(bio);
+ if(baddr)
+ BIO_ADDR_free(baddr);
+ CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
+ return result;
+}
+
+struct h3_quic_recv_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_easy *data;
+ struct cf_osslq_stream *s;
+};
+
+static ssize_t h3_quic_recv(void *reader_ctx,
+ unsigned char *buf, size_t len,
+ CURLcode *err)
+{
+ struct h3_quic_recv_ctx *x = reader_ctx;
+ size_t nread;
+ int rv;
+
+ *err = CURLE_OK;
+ rv = SSL_read_ex(x->s->ssl, buf, len, &nread);
+ if(rv <= 0) {
+ int detail = SSL_get_error(x->s->ssl, rv);
+ if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(detail == SSL_ERROR_ZERO_RETURN) {
+ CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS",
+ x->s->id);
+ x->s->recvd_eos = TRUE;
+ return 0;
+ }
+ else if(SSL_get_stream_read_state(x->s->ssl) ==
+ SSL_STREAM_STATE_RESET_REMOTE) {
+ uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
+ SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
+ CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
+ "rv=%d, app_err=%" PRIu64,
+ x->s->id, rv, app_error_code);
+ if(app_error_code != NGHTTP3_H3_NO_ERROR) {
+ x->s->reset = TRUE;
+ }
+ x->s->recvd_eos = TRUE;
+ return 0;
+ }
+ else {
+ *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
+ return -1;
+ }
+ }
+ else {
+ /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes",
+ x->s->id, nread); */
+ }
+ return (ssize_t)nread;
+}
+
+static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ ssize_t nread;
+ struct h3_quic_recv_ctx x;
+ int rv, eagain = FALSE;
+ size_t total_recv_len = 0;
+
+ DEBUGASSERT(s);
+ if(s->closed)
+ return CURLE_OK;
+
+ x.cf = cf;
+ x.data = data;
+ x.s = s;
+ while(s->ssl && !s->closed && !eagain &&
+ (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
+ if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
+ while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
+ nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result);
+ if(nread < 0) {
+ if(result != CURLE_AGAIN)
+ goto out;
+ result = CURLE_OK;
+ eagain = TRUE;
+ }
+ }
+ }
+
+ /* Forward what we have to nghttp3 */
+ if(!Curl_bufq_is_empty(&s->recvbuf)) {
+ const unsigned char *buf;
+ size_t blen;
+
+ while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
+ nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
+ buf, blen, 0);
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes "
+ "to nghttp3 -> %zd", s->id, blen, nread);
+ if(nread < 0) {
+ failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
+ blen, nghttp3_strerror((int)nread));
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ /* success, `nread` is the flow for QUIC to count as "consumed",
+ * not sure how that will work with OpenSSL. Anyways, without error,
+ * all data that we passed is not owned by nghttp3. */
+ Curl_bufq_skip(&s->recvbuf, blen);
+ total_recv_len += blen;
+ }
+ }
+
+ /* When we forwarded everything, handle RESET/EOS */
+ if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
+ result = CURLE_OK;
+ if(s->reset) {
+ uint64_t app_error;
+ if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
+ failf(data, "SSL_get_stream_read_error_code returned error");
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
+ s->closed = TRUE;
+ if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+ failf(data, "nghttp3_conn_close_stream returned error: %s",
+ nghttp3_strerror(rv));
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ }
+ else if(s->recvd_eos) {
+ rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
+ NGHTTP3_H3_NO_ERROR);
+ s->closed = TRUE;
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d",
+ s->id, rv);
+ if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+ failf(data, "nghttp3_conn_close_stream returned error: %s",
+ nghttp3_strerror(rv));
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ }
+ }
+ }
+out:
+ if(result)
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d",
+ s->id, result);
+ return result;
+}
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(!ctx->tls.ssl)
+ goto out;
+
+ ERR_clear_error();
+
+ /* 1. Check for new incoming streams */
+ while(1) {
+ SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
+ if(!snew)
+ break;
+
+ (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
+ }
+
+ if(!SSL_handle_events(ctx->tls.ssl)) {
+ int detail = SSL_get_error(ctx->tls.ssl, 0);
+ result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
+ }
+
+ if(ctx->h3.conn) {
+ size_t i;
+ for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
+ result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
+ if(result)
+ goto out;
+ }
+ }
+
+ if(ctx->h3.conn) {
+ struct Curl_easy *sdata;
+ struct h3_stream_ctx *stream;
+ /* PULL all open streams */
+ DEBUGASSERT(data->multi);
+ for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
+ stream = H3_STREAM_CTX(sdata);
+ if(stream && !stream->closed &&
+ !Curl_bufq_is_full(&stream->recvbuf)) {
+ result = cf_osslq_stream_recv(&stream->s, cf, sdata);
+ if(result)
+ goto out;
+ }
+ }
+ }
+ }
+
+out:
+ CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
+ return result;
+}
+
+/* Iterate over all streams and check if blocked can be unblocked */
+static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct Curl_easy *sdata;
+ struct h3_stream_ctx *stream;
+
+ if(ctx->h3.conn) {
+ for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ if(sdata->conn == data->conn) {
+ stream = H3_STREAM_CTX(sdata);
+ if(stream && stream->s.ssl && stream->s.send_blocked &&
+ !SSL_want_write(stream->s.ssl)) {
+ nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
+ stream->s.send_blocked = FALSE;
+ h3_drain_stream(cf, sdata);
+ CURL_TRC_CF(sdata, cf, "unblocked");
+ }
+ }
+ }
+ }
+ return CURLE_OK;
+}
+
+static CURLcode h3_send_streams(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(!ctx->tls.ssl || !ctx->h3.conn)
+ goto out;
+
+ for(;;) {
+ struct cf_osslq_stream *s = NULL;
+ nghttp3_vec vec[16];
+ nghttp3_ssize n, i;
+ int64_t stream_id;
+ size_t written;
+ int eos, ok, rv;
+ size_t total_len, acked_len = 0;
+ bool blocked = FALSE;
+
+ n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
+ vec, ARRAYSIZE(vec));
+ if(n < 0) {
+ failf(data, "nghttp3_conn_writev_stream returned error: %s",
+ nghttp3_strerror((int)n));
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ if(stream_id < 0) {
+ result = CURLE_OK;
+ goto out;
+ }
+
+ /* Get the stream for this data */
+ s = cf_osslq_get_qstream(cf, data, stream_id);
+ if(!s) {
+ failf(data, "nghttp3_conn_writev_stream gave unknown stream %" PRId64,
+ stream_id);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ /* Now write the data to the stream's SSL*, it may not all fit! */
+ DEBUGASSERT(s->id == stream_id);
+ for(i = 0, total_len = 0; i < n; ++i) {
+ total_len += vec[i].len;
+ }
+ for(i = 0; (i < n) && !blocked; ++i) {
+ /* Without stream->s.ssl, we closed that already, so
+ * pretend the write did succeed. */
+ written = vec[i].len;
+ ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
+ &written);
+ if(ok) {
+ /* As OpenSSL buffers the data, we count this as acknowledged
+ * from nghttp3's point of view */
+ CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok",
+ s->id, vec[i].len);
+ acked_len += vec[i].len;
+ }
+ else {
+ int detail = SSL_get_error(s->ssl, 0);
+ switch(detail) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ /* QUIC blocked us from writing more */
+ CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
+ s->id, vec[i].len);
+ written = 0;
+ nghttp3_conn_block_stream(ctx->h3.conn, s->id);
+ s->send_blocked = blocked = TRUE;
+ break;
+ default:
+ failf(data, "[%"PRId64"] send %zu bytes to QUIC, SSL error %d",
+ s->id, vec[i].len, detail);
+ result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
+ goto out;
+ }
+ }
+ }
+
+ if(acked_len > 0 || (eos && !s->send_blocked)) {
+ /* Since QUIC buffers the data written internally, we can tell
+ * nghttp3 that it can move forward on it */
+ rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
+ if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
+ if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+ failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes "
+ "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
+ }
+
+ if(eos && !s->send_blocked) {
+ /* wrote everything and H3 indicates end of stream */
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
+ SSL_stream_conclude(s->ssl, 0);
+ }
+ }
+
+out:
+ CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
+ return result;
+}
+
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(!ctx->tls.ssl)
+ goto out;
+
+ ERR_clear_error();
+ result = h3_send_streams(cf, data);
+ if(result)
+ goto out;
+
+ if(!SSL_handle_events(ctx->tls.ssl)) {
+ int detail = SSL_get_error(ctx->tls.ssl, 0);
+ result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
+ }
+
+ result = cf_osslq_check_and_unblock(cf, data);
+
+out:
+ CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
+ return result;
+}
+
+static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct timeval tv;
+ timediff_t timeoutms;
+ int is_infinite = TRUE;
+
+ if(ctx->tls.ssl &&
+ SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
+ !is_infinite) {
+ timeoutms = curlx_tvtoms(&tv);
+ /* QUIC want to be called again latest at the returned timeout */
+ if(timeoutms <= 0) {
+ result = cf_progress_ingress(cf, data);
+ if(result)
+ goto out;
+ result = cf_progress_egress(cf, data);
+ if(result)
+ goto out;
+ if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
+ timeoutms = curlx_tvtoms(&tv);
+ }
+ }
+ if(!is_infinite) {
+ Curl_expire(data, timeoutms, EXPIRE_QUIC);
+ CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
+ }
+ }
+out:
+ return result;
+}
+
+static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+ struct curltime now;
+ int err;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect the UDP filter first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+ 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 */
+ CURL_TRC_CF(data, cf, "waiting for reconnect time");
+ goto out;
+ }
+
+ if(!ctx->tls.ssl) {
+ ctx->started_at = now;
+ result = cf_osslq_ctx_start(cf, data);
+ if(result)
+ goto out;
+ }
+
+ if(!ctx->got_first_byte) {
+ int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
+ if(readable > 0 && (readable & CURL_CSELECT_IN)) {
+ ctx->got_first_byte = TRUE;
+ ctx->first_byte_at = Curl_now();
+ }
+ }
+
+ ERR_clear_error();
+ err = SSL_do_handshake(ctx->tls.ssl);
+
+ if(err == 1) {
+ /* connected */
+ ctx->handshake_at = now;
+ CURL_TRC_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at));
+ result = cf_osslq_verify_peer(cf, data);
+ if(!result) {
+ CURL_TRC_CF(data, cf, "peer verified");
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ }
+ }
+ else {
+ int detail = SSL_get_error(ctx->tls.ssl, err);
+ switch(detail) {
+ case SSL_ERROR_WANT_READ:
+ CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
+ result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
+ goto out;
+ case SSL_ERROR_WANT_WRITE:
+ CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
+ result = CURLE_OK;
+ goto out;
+#ifdef SSL_ERROR_WANT_ASYNC
+ case SSL_ERROR_WANT_ASYNC:
+ CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
+ result = CURLE_OK;
+ goto out;
+#endif
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+ case SSL_ERROR_WANT_RETRY_VERIFY:
+ result = CURLE_OK;
+ goto out;
+#endif
+ default:
+ result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
+ goto out;
+ }
+ }
+
+out:
+ if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
+ /* When a QUIC server instance is shutting down, it may send us a
+ * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+ * state. The CONNECT may work in the near future again. Indicate
+ * that as a "weird" reply. */
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(result) {
+ const char *r_ip = NULL;
+ int r_port = 0;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ infof(data, "QUIC connect to %s port %u failed: %s",
+ r_ip, r_port, curl_easy_strerror(result));
+ }
+#endif
+ if(!result)
+ result = check_and_set_expiry(cf, data);
+ if(result || *done)
+ CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static ssize_t h3_stream_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t len,
+ CURLcode *err)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = NULL;
+ struct dynhds h2_headers;
+ size_t nheader;
+ nghttp3_nv *nva = NULL;
+ int rc = 0;
+ unsigned int i;
+ ssize_t nwritten = -1;
+ nghttp3_data_reader reader;
+ nghttp3_data_reader *preader = NULL;
+
+ Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
+
+ *err = h3_data_setup(cf, data);
+ if(*err)
+ goto out;
+ stream = H3_STREAM_CTX(data);
+ DEBUGASSERT(stream);
+ if(!stream) {
+ *err = CURLE_FAILED_INIT;
+ goto out;
+ }
+
+ nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
+ if(nwritten < 0)
+ goto out;
+ if(!stream->h1.done) {
+ /* need more data */
+ goto out;
+ }
+ DEBUGASSERT(stream->h1.req);
+
+ *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);
+ if(!nva) {
+ *err = CURLE_OUT_OF_MEMORY;
+ nwritten = -1;
+ goto out;
+ }
+
+ for(i = 0; i < nheader; ++i) {
+ struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
+ nva[i].name = (unsigned char *)e->name;
+ nva[i].namelen = e->namelen;
+ nva[i].value = (unsigned char *)e->value;
+ nva[i].valuelen = e->valuelen;
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+ }
+
+ DEBUGASSERT(stream->s.id == -1);
+ *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
+ &ctx->stream_bufcp, data);
+ if(*err) {
+ failf(data, "can't get bidi streams");
+ *err = CURLE_SEND_ERROR;
+ goto out;
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ /* known request body size or -1 */
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown */
+ break;
+ default:
+ /* there is not request body */
+ stream->upload_left = 0; /* no request body */
+ 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->h3.conn, stream->s.id,
+ nva, nheader, preader, data);
+ if(rc) {
+ switch(rc) {
+ case NGHTTP3_ERR_CONN_CLOSING:
+ CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+ "connection is closing", stream->s.id);
+ break;
+ default:
+ CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+ stream->s.id, rc, nghttp3_strerror(rc));
+ break;
+ }
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ goto out;
+ }
+
+ if(Curl_trc_is_verbose(data)) {
+ infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+ stream->s.id, data->state.url);
+ for(i = 0; i < nheader; ++i) {
+ infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id,
+ (int)nva[i].namelen, nva[i].name,
+ (int)nva[i].valuelen, nva[i].value);
+ }
+ }
+
+out:
+ free(nva);
+ Curl_dynhds_free(&h2_headers);
+ return nwritten;
+}
+
+static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_call_data save;
+ ssize_t nwritten;
+ CURLcode result;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx->tls.ssl);
+ DEBUGASSERT(ctx->h3.conn);
+ *err = CURLE_OK;
+
+ result = cf_progress_ingress(cf, data);
+ if(result) {
+ *err = result;
+ nwritten = -1;
+ goto out;
+ }
+
+ result = cf_progress_egress(cf, data);
+ if(result) {
+ *err = result;
+ nwritten = -1;
+ goto out;
+ }
+
+ if(!stream || stream->s.id < 0) {
+ nwritten = h3_stream_open(cf, data, buf, len, err);
+ if(nwritten < 0) {
+ 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;
+ nwritten = -1;
+ goto out;
+ }
+ nwritten = (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->s.id);
+ *err = CURLE_OK;
+ nwritten = (ssize_t)len;
+ goto out;
+ }
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> stream closed", stream->s.id, len);
+ *err = CURLE_HTTP3;
+ nwritten = -1;
+ goto out;
+ }
+ else {
+ nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
+ "sendbuf(len=%zu) -> %zd, %d",
+ stream->s.id, len, nwritten, *err);
+ if(nwritten < 0) {
+ goto out;
+ }
+
+ (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
+ }
+
+ result = cf_progress_egress(cf, data);
+ if(result) {
+ *err = result;
+ nwritten = -1;
+ }
+
+ if(stream && nwritten > 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. */
+ stream->upload_blocked_len = nwritten;
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
+ "%zu bytes in flight -> EGAIN", stream->s.id, len,
+ stream->sendbuf_len_in_flight);
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ }
+
+out:
+ result = check_and_set_expiry(cf, data);
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ stream? stream->s.id : -1, len, nwritten, *err);
+ CF_DATA_RESTORE(cf, save);
+ return nwritten;
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h3_stream_ctx *stream,
+ CURLcode *err)
+{
+ ssize_t nread = -1;
+
+ (void)cf;
+ if(stream->reset) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
+ *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
+ goto out;
+ }
+ else if(!stream->resp_hds_complete) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->s.id);
+ *err = CURLE_HTTP3;
+ goto out;
+ }
+ *err = CURLE_OK;
+ nread = 0;
+
+out:
+ return nread;
+}
+
+static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ ssize_t nread = -1;
+ struct cf_call_data save;
+ CURLcode result;
+
+ (void)ctx;
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(ctx->tls.ssl);
+ DEBUGASSERT(ctx->h3.conn);
+ *err = CURLE_OK;
+
+ if(!stream) {
+ *err = CURLE_RECV_ERROR;
+ goto out;
+ }
+
+ if(!Curl_bufq_is_empty(&stream->recvbuf)) {
+ nread = Curl_bufq_read(&stream->recvbuf,
+ (unsigned char *)buf, len, err);
+ if(nread < 0) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->s.id, len, nread, *err);
+ goto out;
+ }
+ }
+
+ result = cf_progress_ingress(cf, data);
+ if(result) {
+ *err = result;
+ nread = -1;
+ goto out;
+ }
+
+ /* recvbuf had nothing before, maybe after progressing ingress? */
+ if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
+ nread = Curl_bufq_read(&stream->recvbuf,
+ (unsigned char *)buf, len, err);
+ if(nread < 0) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->s.id, len, nread, *err);
+ goto out;
+ }
+ }
+
+ if(nread > 0) {
+ h3_drain_stream(cf, data);
+ }
+ else {
+ if(stream->closed) {
+ nread = recv_closed_stream(cf, data, stream, err);
+ goto out;
+ }
+ *err = CURLE_AGAIN;
+ nread = -1;
+ }
+
+out:
+ if(cf_progress_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ nread = -1;
+ }
+ else {
+ CURLcode result2 = check_and_set_expiry(cf, data);
+ if(result2) {
+ *err = result2;
+ nread = -1;
+ }
+ }
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
+ stream? stream->s.id : -1, len, nread, *err);
+ CF_DATA_RESTORE(cf, save);
+ return nread;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ (void)cf;
+ return stream && !Curl_bufq_is_empty(&stream->recvbuf);
+}
+
+static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_SETUP:
+ break;
+ case CF_CTRL_DATA_PAUSE:
+ result = h3_data_pause(cf, data, (arg1 != 0));
+ break;
+ case CF_CTRL_DATA_DETACH:
+ h3_data_done(cf, data);
+ break;
+ case CF_CTRL_DATA_DONE:
+ h3_data_done(cf, data);
+ break;
+ case CF_CTRL_DATA_DONE_SEND: {
+ 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);
+ (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
+ }
+ break;
+ }
+ 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);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ bool alive = FALSE;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ *input_pending = FALSE;
+ if(!ctx->tls.ssl)
+ goto out;
+
+ /* TODO: how to check negotiated connection idle time? */
+
+ if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+ goto out;
+
+ alive = TRUE;
+ if(*input_pending) {
+ CURLcode result;
+ /* This happens before we'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" */
+ *input_pending = FALSE;
+ result = cf_progress_ingress(cf, data);
+ CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
+ alive = result? FALSE : TRUE;
+ }
+
+out:
+ CF_DATA_RESTORE(cf, save);
+ return alive;
+}
+
+static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct easy_pollset *ps)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+
+ if(!ctx->tls.ssl) {
+ /* NOP */
+ }
+ else if(!cf->connected) {
+ /* during handshake, transfer has not started yet. we always
+ * add our socket for polling if SSL wants to send/recv */
+ Curl_pollset_set(data, ps, ctx->q.sockfd,
+ SSL_net_read_desired(ctx->tls.ssl),
+ SSL_net_write_desired(ctx->tls.ssl));
+ }
+ else {
+ /* once connected, we only modify the socket if it is present.
+ * this avoids adding it for paused transfers. */
+ bool want_recv, want_send;
+ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+ if(want_recv || want_send) {
+ Curl_pollset_set(data, ps, ctx->q.sockfd,
+ SSL_net_read_desired(ctx->tls.ssl),
+ SSL_net_write_desired(ctx->tls.ssl));
+ }
+ }
+}
+
+static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ /* TODO: how to get this? */
+ CF_DATA_SAVE(save, cf, data);
+ *pres1 = 100;
+ CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
+ CF_DATA_RESTORE(cf, save);
+ return CURLE_OK;
+ }
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ if(ctx->got_first_byte)
+ *when = ctx->first_byte_at;
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ if(cf->connected)
+ *when = ctx->handshake_at;
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_osslq_destroy,
+ cf_osslq_connect,
+ cf_osslq_close,
+ Curl_cf_def_get_host,
+ cf_osslq_adjust_pollset,
+ cf_osslq_data_pending,
+ cf_osslq_send,
+ cf_osslq_recv,
+ cf_osslq_data_event,
+ cf_osslq_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_osslq_query,
+};
+
+CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_osslq_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ ctx = calloc(1, sizeof(*ctx));
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_osslq_ctx_clear(ctx);
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+ if(result)
+ goto out;
+
+ result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+ if(result)
+ goto out;
+
+ cf->conn = conn;
+ udp_cf->conn = cf->conn;
+ udp_cf->sockindex = cf->sockindex;
+ cf->next = udp_cf;
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ if(udp_cf)
+ Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+ return result;
+}
+
+bool Curl_conn_is_osslq(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+/*
+ * Store ngtcp2 version info in this buffer.
+ */
+void Curl_osslq_ver(char *p, size_t len)
+{
+ const nghttp3_info *ht3 = nghttp3_version(0);
+ (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str);
+}
+
+#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */
diff --git a/libs/libcurl/src/vquic/curl_osslq.h b/libs/libcurl/src/vquic/curl_osslq.h
new file mode 100644
index 0000000000..f34d84baf8
--- /dev/null
+++ b/libs/libcurl/src/vquic/curl_osslq.h
@@ -0,0 +1,51 @@
+#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H
+#define HEADER_CURL_VQUIC_CURL_OSSLQ_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+struct Curl_cfilter;
+
+#include "urldata.h"
+
+void Curl_osslq_ver(char *p, size_t len);
+
+CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_osslq(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+#endif
+
+#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c
index 9cf0617f11..263b9180f7 100644
--- a/libs/libcurl/src/vquic/curl_quiche.c
+++ b/libs/libcurl/src/vquic/curl_quiche.c
@@ -43,6 +43,7 @@
#include "http1.h"
#include "vquic.h"
#include "vquic_int.h"
+#include "vquic-tls.h"
#include "curl_quiche.h"
#include "transfer.h"
#include "inet_pton.h"
@@ -84,31 +85,22 @@ void Curl_quiche_ver(char *p, size_t len)
(void)msnprintf(p, len, "quiche/%s", quiche_version());
}
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-
struct cf_quiche_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
+ struct quic_tls_ctx tls;
quiche_conn *qconn;
quiche_config *cfg;
quiche_h3_conn *h3c;
quiche_h3_config *h3config;
uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
- SSL_CTX *sslctx;
- SSL *ssl;
struct curltime started_at; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct curltime first_byte_at; /* when first byte was recvd */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
curl_off_t data_recvd;
uint64_t max_idle_ms; /* max idle time for QUIC conn */
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 */
};
@@ -123,121 +115,25 @@ static void quiche_debug_log(const char *line, void *argp)
static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
{
if(ctx) {
- vquic_ctx_free(&ctx->q);
- if(ctx->qconn)
- quiche_conn_free(ctx->qconn);
- if(ctx->h3config)
- quiche_h3_config_free(ctx->h3config);
if(ctx->h3c)
quiche_h3_conn_free(ctx->h3c);
+ if(ctx->h3config)
+ quiche_h3_config_free(ctx->h3config);
+ if(ctx->qconn)
+ quiche_conn_free(ctx->qconn);
if(ctx->cfg)
quiche_config_free(ctx->cfg);
- Curl_bufcp_free(&ctx->stream_bufcp);
+ /* quiche just freed ctx->tls.ssl */
+ ctx->tls.ssl = NULL;
+ Curl_vquic_tls_cleanup(&ctx->tls);
Curl_ssl_peer_cleanup(&ctx->peer);
+ vquic_ctx_free(&ctx->q);
+ Curl_bufcp_free(&ctx->stream_bufcp);
memset(ctx, 0, sizeof(*ctx));
}
}
-static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_quiche_ctx *ctx = cf->ctx;
- struct ssl_primary_config *conn_config;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
-
- if(!ctx->x509_store_setup) {
- if(conn_config->verifypeer) {
- const char * const ssl_cafile = conn_config->CAfile;
- const char * const ssl_capath = conn_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(ctx->sslctx);
- }
-#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;
- struct ssl_primary_config *conn_config;
- CURLcode result;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
-
- result = Curl_ssl_peer_init(&ctx->peer, cf);
- if(result)
- return result;
-
- 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);
- }
-
- if(conn_config->curves &&
- !SSL_CTX_set1_curves_list(ctx->sslctx, conn_config->curves)) {
- failf(data, "failed setting curves list for QUIC: '%s'",
- conn_config->curves);
- return CURLE_SSL_CIPHER;
- }
-
- ctx->ssl = SSL_new(ctx->sslctx);
- if(!ctx->ssl)
- return CURLE_QUIC_CONNECT_ERROR;
-
- SSL_set_app_data(ctx->ssl, cf);
-
- if(ctx->peer.sni) {
- if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) {
- 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
*/
@@ -337,8 +233,8 @@ static void drain_stream(struct Curl_cfilter *cf,
bits = CURL_CSELECT_IN;
if(stream && !stream->send_closed && stream->upload_left)
bits |= CURL_CSELECT_OUT;
- if(data->state.dselect_bits != bits) {
- data->state.dselect_bits = bits;
+ if(data->state.select_bits != bits) {
+ data->state.select_bits = bits;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
@@ -668,7 +564,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
- long verify_ok = SSL_get_verify_result(ctx->ssl);
+ long verify_ok = SSL_get_verify_result(ctx->tls.ssl);
if(verify_ok != X509_V_OK) {
failf(r->data, "SSL certificate problem: %s",
X509_verify_cert_error_string(verify_ok));
@@ -696,7 +592,7 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
CURLcode result;
DEBUGASSERT(ctx->qconn);
- result = quic_x509_store_setup(cf, data);
+ result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
if(result)
return result;
@@ -836,7 +732,7 @@ 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 = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_HTTP3;
CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d",
stream->id, *err);
}
@@ -846,7 +742,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
" all response header fields, treated as error",
stream->id);
/* *err = CURLE_PARTIAL_FILE; */
- *err = CURLE_RECV_ERROR;
+ *err = CURLE_HTTP3;
CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete"
" -> %d", stream->id, *err);
}
@@ -1078,6 +974,28 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
goto out;
stream = H3_STREAM_CTX(data);
}
+ else if(stream->closed) {
+ if(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;
+ }
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> stream closed", stream->id, len);
+ *err = CURLE_HTTP3;
+ nwritten = -1;
+ goto out;
+ }
else {
bool eof = (stream->upload_left >= 0 &&
(curl_off_t)len >= stream->upload_left);
@@ -1095,20 +1013,11 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
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;
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> invalid stream state", stream->id, len);
+ *err = CURLE_HTTP3;
+ nwritten = -1;
goto out;
}
else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
@@ -1167,16 +1076,19 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf,
struct easy_pollset *ps)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- bool want_recv = CURL_WANT_RECV(data);
- bool want_send = CURL_WANT_SEND(data);
+ bool want_recv, want_send;
+
+ if(!ctx->qconn)
+ return;
- if(ctx->qconn && (want_recv || want_send)) {
+ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+ if(want_recv || want_send) {
struct stream_ctx *stream = H3_STREAM_CTX(data);
bool c_exhaust, s_exhaust;
c_exhaust = FALSE; /* Have not found any call in quiche that tells
us if the connection itself is blocked */
- s_exhaust = stream && stream->id >= 0 &&
+ s_exhaust = want_send && stream && stream->id >= 0 &&
(stream->quic_flow_blocked || !stream_is_writeable(cf, data));
want_recv = (want_recv || c_exhaust || s_exhaust);
want_send = (!s_exhaust && want_send) ||
@@ -1261,66 +1173,6 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
return result;
}
-static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_quiche_ctx *ctx = cf->ctx;
- struct ssl_primary_config *conn_config;
- CURLcode result = CURLE_OK;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
-
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->httpversion = 30;
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-
- if(conn_config->verifyhost) {
- X509 *server_cert;
- server_cert = SSL_get_peer_certificate(ctx->ssl);
- if(!server_cert) {
- result = CURLE_PEER_FAILED_VERIFICATION;
- goto out;
- }
- result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert);
- X509_free(server_cert);
- if(result)
- goto out;
- }
- else
- CURL_TRC_CF(data, cf, "Skipped certificate verification");
-
- ctx->h3config = quiche_h3_config_new();
- if(!ctx->h3config) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- /* Create a new HTTP/3 connection on the QUIC connection. */
- ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
- if(!ctx->h3c) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, ctx->ssl);
-
-out:
- if(result) {
- if(ctx->h3config) {
- quiche_h3_config_free(ctx->h3config);
- ctx->h3config = NULL;
- }
- if(ctx->h3c) {
- quiche_h3_conn_free(ctx->h3c);
- ctx->h3c = NULL;
- }
- }
- return result;
-}
-
static CURLcode cf_connect_start(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -1348,6 +1200,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
if(result)
return result;
+ result = Curl_ssl_peer_init(&ctx->peer, cf);
+ if(result)
+ return result;
+
ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
if(!ctx->cfg) {
failf(data, "can't create quiche config");
@@ -1376,9 +1232,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- 1);
- DEBUGASSERT(!ctx->ssl);
- DEBUGASSERT(!ctx->sslctx);
- result = quic_ssl_setup(cf, data);
+ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+ QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
+ NULL, cf);
if(result)
return result;
@@ -1399,7 +1256,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
(struct sockaddr *)&ctx->q.local_addr,
ctx->q.local_addrlen,
&sockaddr->sa_addr, sockaddr->addrlen,
- ctx->cfg, ctx->ssl, false);
+ ctx->cfg, ctx->tls.ssl, false);
if(!ctx->qconn) {
failf(data, "can't create quiche connection");
return CURLE_OUT_OF_MEMORY;
@@ -1438,6 +1295,18 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
return CURLE_OK;
}
+static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
+}
+
static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
@@ -1489,9 +1358,21 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
ctx->handshake_at = ctx->q.last_op;
CURL_TRC_CF(data, cf, "handshake complete after %dms",
(int)Curl_timediff(ctx->handshake_at, ctx->started_at));
- result = cf_verify_peer(cf, data);
+ result = cf_quiche_verify_peer(cf, data);
if(!result) {
CURL_TRC_CF(data, cf, "peer verified");
+ ctx->h3config = quiche_h3_config_new();
+ if(!ctx->h3config) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Create a new HTTP/3 connection on the QUIC connection. */
+ ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
+ if(!ctx->h3c) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
@@ -1564,8 +1445,8 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
- if(ctx->got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ if(ctx->q.got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
}
else
@@ -1573,8 +1454,8 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
return CURLE_OK;
case CF_QUERY_TIMER_CONNECT: {
struct curltime *when = pres2;
- if(ctx->got_first_byte)
- *when = ctx->first_byte_at;
+ if(ctx->q.got_first_byte)
+ *when = ctx->q.first_byte_at;
return CURLE_OK;
}
case CF_QUERY_TIMER_APPCONNECT: {
diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c
new file mode 100644
index 0000000000..29c23ef58c
--- /dev/null
+++ b/libs/libcurl/src/vquic/vquic-tls.c
@@ -0,0 +1,609 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(ENABLE_QUIC) && \
+ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#include "vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+#include <nettle/sha2.h>
+#include "vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#include "vtls/wolfssl.h"
+#endif
+
+#include "urldata.h"
+#include "curl_trc.h"
+#include "cfilters.h"
+#include "multiif.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "vquic-tls.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#ifdef USE_OPENSSL
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+#elif defined(USE_GNUTLS)
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+#elif defined(USE_WOLFSSL)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+#endif
+
+
+#ifdef USE_OPENSSL
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+
+static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ Curl_vquic_tls_ctx_setup *ctx_setup)
+{
+ struct ssl_primary_config *conn_config;
+ CURLcode result = CURLE_FAILED_INIT;
+
+ DEBUGASSERT(!ctx->ssl_ctx);
+#ifdef USE_OPENSSL_QUIC
+ ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
+#else
+ ctx->ssl_ctx = SSL_CTX_new(TLS_method());
+#endif
+ if(!ctx->ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config) {
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+
+ if(ctx_setup) {
+ result = ctx_setup(ctx, cf, data);
+ if(result)
+ goto out;
+ }
+
+ SSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+
+ {
+ const char *curves = conn_config->curves ?
+ conn_config->curves : QUIC_GROUPS;
+ if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) {
+ failf(data, "failed setting curves list for QUIC: '%s'", curves);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+
+#ifndef OPENSSL_IS_BORINGSSL
+ {
+ const char *ciphers13 = conn_config->cipher_list13 ?
+ conn_config->cipher_list13 : QUIC_CIPHERS;
+ if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) {
+ failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "QUIC cipher selection: %s", ciphers13);
+ }
+#endif
+
+ /* 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->ssl_ctx, keylog_callback);
+ }
+
+ /* 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
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ /* 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, ctx->ssl_ctx);
+ if(result)
+ goto out;
+ ctx->x509_store_setup = TRUE;
+ }
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ if(result && ctx->ssl_ctx) {
+ SSL_CTX_free(ctx->ssl_ctx);
+ ctx->ssl_ctx = NULL;
+ }
+ return result;
+}
+
+static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ SSL_CTX *ssl_ctx = ctx->ssl_ctx;
+ const struct ssl_config_data *ssl_config;
+
+ ssl_config = Curl_ssl_cf_get_config(cf, data);
+ DEBUGASSERT(ssl_config);
+
+ if(ssl_config->primary.clientcert ||
+ ssl_config->primary.cert_blob ||
+ ssl_config->cert_type) {
+ return Curl_ossl_set_client_cert(
+ data, ssl_ctx, ssl_config->primary.clientcert,
+ ssl_config->primary.cert_blob, ssl_config->cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd);
+ }
+
+ return CURLE_OK;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const char *alpn, size_t alpn_len,
+ void *user_data)
+{
+ DEBUGASSERT(!ctx->ssl);
+ ctx->ssl = SSL_new(ctx->ssl_ctx);
+
+ SSL_set_app_data(ctx->ssl, user_data);
+ SSL_set_connect_state(ctx->ssl);
+#ifndef USE_OPENSSL_QUIC
+ SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+#endif
+
+ if(alpn)
+ SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len);
+
+ if(peer->sni) {
+ if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) {
+ failf(data, "Failed set SNI");
+ SSL_free(ctx->ssl);
+ ctx->ssl = NULL;
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+ }
+ return CURLE_OK;
+}
+
+#elif defined(USE_GNUTLS)
+static int keylog_callback(gnutls_session_t session, const char *label,
+ const gnutls_datum_t *secret)
+{
+ gnutls_datum_t crandom;
+ gnutls_datum_t srandom;
+
+ gnutls_session_get_random(session, &crandom, &srandom);
+ if(crandom.size != 32) {
+ return -1;
+ }
+
+ Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+ return 0;
+}
+
+static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const char *alpn, size_t alpn_len,
+ Curl_vquic_tls_ctx_setup *ctx_setup,
+ void *user_data)
+{
+ struct ssl_primary_config *conn_config;
+ CURLcode result;
+ gnutls_datum_t alpns[5];
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ long * const pverifyresult = &data->set.ssl.certverifyresult;
+ int rc;
+
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config)
+ return CURLE_FAILED_INIT;
+
+ DEBUGASSERT(ctx->gtls == NULL);
+ ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
+ if(!ctx->gtls)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = gtls_client_init(data, conn_config, &data->set.ssl,
+ peer, ctx->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ gnutls_session_set_ptr(ctx->gtls->session, user_data);
+
+ if(ctx_setup) {
+ result = ctx_setup(ctx, cf, data);
+ if(result)
+ return result;
+ }
+
+ rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
+ if(rc < 0) {
+ CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+ gnutls_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
+ }
+
+ /* convert the ALPN string from our arguments to a list of strings
+ * that gnutls wants and will convert internally back to this very
+ * string for sending to the server. nice. */
+ if(alpn) {
+ size_t i, alen = alpn_len;
+ unsigned char *s = (unsigned char *)alpn;
+ unsigned char slen;
+ for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
+ slen = s[0];
+ if(slen >= alen)
+ return CURLE_FAILED_INIT;
+ alpns[i].data = s + 1;
+ alpns[i].size = slen;
+ s += slen + 1;
+ alen -= (size_t)slen + 1;
+ }
+ if(alen) /* not all alpn chars used, wrong format or too many */
+ return CURLE_FAILED_INIT;
+ if(i) {
+ gnutls_alpn_set_protocols(ctx->gtls->session,
+ alpns, (unsigned int)i,
+ GNUTLS_ALPN_MANDATORY);
+ }
+ }
+
+ return CURLE_OK;
+}
+#elif defined(USE_WOLFSSL)
+
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#endif
+
+static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ Curl_vquic_tls_ctx_setup *ctx_setup)
+{
+ struct ssl_primary_config *conn_config;
+ CURLcode result = CURLE_FAILED_INIT;
+
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config) {
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+
+ ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+ if(!ctx->ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if(ctx_setup) {
+ result = ctx_setup(ctx, cf, data);
+ if(result)
+ goto out;
+ }
+
+ wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+
+ if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ?
+ conn_config->cipher_list13 :
+ QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
+ goto out;
+ }
+
+ if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ?
+ conn_config->curves :
+ (char *)QUIC_GROUPS) != 1) {
+ failf(data, "wolfSSL failed to set curves");
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+#if defined(HAVE_SECRET_CALLBACK)
+ wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
+#else
+ failf(data, "wolfSSL was built without keylog callback");
+ goto out;
+#endif
+ }
+
+ if(conn_config->verifypeer) {
+ const char * const ssl_cafile = conn_config->CAfile;
+ const char * const ssl_capath = conn_config->CApath;
+
+ wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
+ if(ssl_cafile || ssl_capath) {
+ /* tell wolfSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ int rc =
+ wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile,
+ ssl_capath,
+ WOLFSSL_LOAD_FLAG_IGNORE_ERR);
+ if(SSL_SUCCESS != rc) {
+ /* 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");
+ goto out;
+ }
+ 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 wolfssl's built-in default as fallback */
+ wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+ }
+#endif
+ }
+ else {
+ wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ if(result && ctx->ssl_ctx) {
+ SSL_CTX_free(ctx->ssl_ctx);
+ ctx->ssl_ctx = NULL;
+ }
+ return result;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const char *alpn, size_t alpn_len,
+ void *user_data)
+{
+ (void)data;
+ DEBUGASSERT(!ctx->ssl);
+ DEBUGASSERT(ctx->ssl_ctx);
+ ctx->ssl = wolfSSL_new(ctx->ssl_ctx);
+
+ wolfSSL_set_app_data(ctx->ssl, user_data);
+ wolfSSL_set_connect_state(ctx->ssl);
+ wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+ if(alpn)
+ wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn,
+ (int)alpn_len);
+
+ if(peer->sni) {
+ wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
+ peer->sni, (unsigned short)strlen(peer->sni));
+ }
+
+ return CURLE_OK;
+}
+#endif /* defined(USE_WOLFSSL) */
+
+CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const char *alpn, size_t alpn_len,
+ Curl_vquic_tls_ctx_setup *ctx_setup,
+ void *user_data)
+{
+ CURLcode result;
+
+#ifdef USE_OPENSSL
+ result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup);
+ if(result)
+ return result;
+
+ result = curl_ossl_set_client_cert(ctx, cf, data);
+ if(result)
+ return result;
+
+ return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+#elif defined(USE_GNUTLS)
+ (void)result;
+ return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len,
+ ctx_setup, user_data);
+#elif defined(USE_WOLFSSL)
+ result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup);
+ if(result)
+ return result;
+
+ return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+#else
+#error "no TLS lib in used, should not happen"
+ return CURLE_FAILED_INIT;
+#endif
+}
+
+void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
+{
+#ifdef USE_OPENSSL
+ if(ctx->ssl)
+ SSL_free(ctx->ssl);
+ if(ctx->ssl_ctx)
+ SSL_CTX_free(ctx->ssl_ctx);
+#elif defined(USE_GNUTLS)
+ if(ctx->gtls) {
+ if(ctx->gtls->cred)
+ gnutls_certificate_free_credentials(ctx->gtls->cred);
+ if(ctx->gtls->session)
+ gnutls_deinit(ctx->gtls->session);
+ free(ctx->gtls);
+ }
+#elif defined(USE_WOLFSSL)
+ if(ctx->ssl)
+ wolfSSL_free(ctx->ssl);
+ if(ctx->ssl_ctx)
+ wolfSSL_CTX_free(ctx->ssl_ctx);
+#endif
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL
+ if(!ctx->x509_store_setup) {
+ CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
+ if(result)
+ return result;
+ ctx->x509_store_setup = TRUE;
+ }
+#else
+ (void)ctx; (void)cf; (void)data;
+#endif
+ return CURLE_OK;
+}
+
+CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer)
+{
+ struct ssl_primary_config *conn_config;
+ CURLcode result = CURLE_OK;
+
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config)
+ return CURLE_FAILED_INIT;
+
+ if(conn_config->verifyhost) {
+#ifdef USE_OPENSSL
+ X509 *server_cert;
+ server_cert = SSL_get1_peer_certificate(ctx->ssl);
+ if(!server_cert) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert);
+ X509_free(server_cert);
+ if(result)
+ return result;
+#elif defined(USE_GNUTLS)
+ result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+ conn_config, &data->set.ssl, peer,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ if(!peer->sni ||
+ wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE)
+ return CURLE_PEER_FAILED_VERIFICATION;
+#endif
+ infof(data, "Verified certificate just fine");
+ }
+ else
+ infof(data, "Skipped certificate verification");
+#ifdef USE_OPENSSL
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, ctx->ssl);
+#endif
+ return result;
+}
+
+
+#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
diff --git a/libs/libcurl/src/vquic/vquic-tls.h b/libs/libcurl/src/vquic/vquic-tls.h
new file mode 100644
index 0000000000..60dc7b18b6
--- /dev/null
+++ b/libs/libcurl/src/vquic/vquic-tls.h
@@ -0,0 +1,98 @@
+#ifndef HEADER_CURL_VQUIC_TLS_H
+#define HEADER_CURL_VQUIC_TLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "bufq.h"
+
+#if defined(ENABLE_QUIC) && \
+ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+
+struct quic_tls_ctx {
+#ifdef USE_OPENSSL
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+#elif defined(USE_GNUTLS)
+ struct gtls_instance *gtls;
+#elif defined(USE_WOLFSSL)
+ WOLFSSL_CTX *ssl_ctx;
+ WOLFSSL *ssl;
+#endif
+ BIT(x509_store_setup); /* if x509 store has been set up */
+};
+
+/**
+ * Callback passed to `Curl_vquic_tls_init()` that can
+ * do early initializations on the not otherwise configured TLS
+ * instances created. This varies by TLS backend:
+ * - openssl/wolfssl: SSL_CTX* has just been created
+ * - gnutls: gtls_client_init() has run
+ */
+typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * Initialize the QUIC TLS instances based of the SSL configurations
+ * for the connection filter, transfer and peer.
+ * @param ctx the TLS context to initialize
+ * @param cf the connection filter involved
+ * @param data the transfer involved
+ * @param peer the peer that will be connected to
+ * @param alpn the ALPN string in protocol format ((len+bytes+)+),
+ * may be NULL
+ * @param alpn_len the overall number of bytes in `alpn`
+ * @param ctx_setup optional callback for very early TLS config
+ * @param user_data optional pointer to set in TLS application context
+ */
+CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const char *alpn, size_t alpn_len,
+ Curl_vquic_tls_ctx_setup *ctx_setup,
+ void *user_data);
+
+/**
+ * Cleanup all data that has been initialized.
+ */
+void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx);
+
+CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * After the QUIC basic handshake has been, verify that the peer
+ * (and its certificate) fulfill our requirements.
+ */
+CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer);
+
+#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
+
+#endif /* HEADER_CURL_VQUIC_TLS_H */
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c
index 8b9c1ae223..8a6bb99aac 100644
--- a/libs/libcurl/src/vquic/vquic.c
+++ b/libs/libcurl/src/vquic/vquic.c
@@ -46,6 +46,7 @@
#include "curl_trc.h"
#include "curl_msh3.h"
#include "curl_ngtcp2.h"
+#include "curl_osslq.h"
#include "curl_quiche.h"
#include "rand.h"
#include "vquic.h"
@@ -74,6 +75,8 @@ void Curl_quic_ver(char *p, size_t len)
{
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
Curl_ngtcp2_ver(p, len);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+ Curl_osslq_ver(p, len);
#elif defined(USE_QUICHE)
Curl_quiche_ver(p, len);
#elif defined(USE_MSH3)
@@ -179,7 +182,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
qctx->no_gso = TRUE;
return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
default:
failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
return CURLE_SEND_ERROR;
@@ -543,8 +546,13 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf,
#else
result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
#endif
- if(!result)
+ if(!result) {
+ if(!qctx->got_first_byte) {
+ qctx->got_first_byte = TRUE;
+ qctx->first_byte_at = qctx->last_op;
+ }
qctx->last_io = qctx->last_op;
+ }
return result;
}
@@ -603,6 +611,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
DEBUGASSERT(transport == TRNSPRT_QUIC);
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+ return Curl_cf_osslq_create(pcf, data, conn, ai);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_create(pcf, data, conn, ai);
#elif defined(USE_MSH3)
@@ -622,6 +632,8 @@ bool Curl_conn_is_http3(const struct Curl_easy *data,
{
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_conn_is_ngtcp2(data, conn, sockindex);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+ return Curl_conn_is_osslq(data, conn, sockindex);
#elif defined(USE_QUICHE)
return Curl_conn_is_quiche(data, conn, sockindex);
#elif defined(USE_MSH3)
diff --git a/libs/libcurl/src/vquic/vquic_int.h b/libs/libcurl/src/vquic/vquic_int.h
index dc50593cd9..ce7983029a 100644
--- a/libs/libcurl/src/vquic/vquic_int.h
+++ b/libs/libcurl/src/vquic/vquic_int.h
@@ -40,6 +40,7 @@ struct cf_quic_ctx {
socklen_t local_addrlen; /* length of local address */
struct bufq sendbuf; /* buffer for sending one or more packets */
+ struct curltime first_byte_at; /* when first byte was recvd */
struct curltime last_op; /* last (attempted) send/recv operation */
struct curltime last_io; /* last successful socket IO */
size_t gsolen; /* length of individual packets in send buf */
@@ -48,7 +49,8 @@ struct cf_quic_ctx {
#ifdef DEBUGBUILD
int wblock_percent; /* percent of writes doing EAGAIN */
#endif
- bool no_gso; /* do not use gso on sending */
+ BIT(got_first_byte); /* if first byte was received */
+ BIT(no_gso); /* do not use gso on sending */
};
CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
diff --git a/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c
index 71a64cd5e8..59b8bf872b 100644
--- a/libs/libcurl/src/vssh/libssh.c
+++ b/libs/libcurl/src/vssh/libssh.c
@@ -31,6 +31,8 @@
#include <limits.h>
+/* in 0.10.0 or later, ignore deprecated warnings */
+#define SSH_SUPPRESS_DEPRECATED
#include <libssh/libssh.h>
#include <libssh/sftp.h>
@@ -89,14 +91,6 @@
#include "curl_memory.h"
#include "memdebug.h"
-/* in 0.10.0 or later, ignore deprecated warnings */
-#if defined(__GNUC__) && \
- (LIBSSH_VERSION_MINOR >= 10) || \
- (LIBSSH_VERSION_MAJOR > 0)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
/* A recent macro provided by libssh. Or make our own. */
#ifndef SSH_STRING_FREE_CHAR
#define SSH_STRING_FREE_CHAR(x) \
@@ -167,7 +161,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* domore_getsock */
myssh_getsock, /* perform_getsock */
scp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
@@ -194,7 +188,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* domore_getsock */
myssh_getsock, /* perform_getsock */
sftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
@@ -445,11 +439,8 @@ static int myssh_is_known(struct Curl_easy *data)
keymatch = CURLKHMATCH_OK;
break;
case SSH_KNOWN_HOSTS_OTHER:
- /* fallthrough */
case SSH_KNOWN_HOSTS_NOT_FOUND:
- /* fallthrough */
case SSH_KNOWN_HOSTS_UNKNOWN:
- /* fallthrough */
case SSH_KNOWN_HOSTS_ERROR:
keymatch = CURLKHMATCH_MISSING;
break;
@@ -465,7 +456,6 @@ static int myssh_is_known(struct Curl_easy *data)
keymatch = CURLKHMATCH_OK;
break;
case SSH_SERVER_FILE_NOT_FOUND:
- /* fallthrough */
case SSH_SERVER_NOT_KNOWN:
keymatch = CURLKHMATCH_MISSING;
break;
@@ -629,7 +619,7 @@ restart:
if(rc < 0)
return SSH_ERROR;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 1:
sshc->kbd_state = 1;
@@ -704,7 +694,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
ssh_set_blocking(sshc->ssh_session, 0);
state(data, SSH_S_STARTUP);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_S_STARTUP:
rc = ssh_connect(sshc->ssh_session);
@@ -719,7 +709,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
state(data, SSH_HOSTKEY);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_HOSTKEY:
rc = myssh_is_known(data);
@@ -729,7 +719,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
state(data, SSH_AUTHLIST);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_AUTHLIST:{
sshc->authed = FALSE;
@@ -910,7 +900,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
state(data, SSH_AUTH_PASS);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_AUTH_PASS:
rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
@@ -973,7 +963,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
state(data, SSH_SFTP_REALPATH);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_SFTP_REALPATH:
/*
* Get the "home" directory
@@ -1161,22 +1151,22 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(statvfs) {
#ifdef _MSC_VER
- #define LIBSSH_VFS_SIZE_MASK "I64u"
+ #define CURL_LIBSSH_VFS_SIZE_MASK "I64u"
#else
- #define LIBSSH_VFS_SIZE_MASK PRIu64
+ #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64
#endif
char *tmp = aprintf("statvfs:\n"
- "f_bsize: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_frsize: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_blocks: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_bfree: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_bavail: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_files: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_ffree: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_favail: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_fsid: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_flag: %" LIBSSH_VFS_SIZE_MASK "\n"
- "f_namemax: %" LIBSSH_VFS_SIZE_MASK "\n",
+ "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+ "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n",
statvfs->f_bsize, statvfs->f_frsize,
statvfs->f_blocks, statvfs->f_bfree,
statvfs->f_bavail, statvfs->f_files,
@@ -1318,13 +1308,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
- curlx_sotouz(data->state.resume_from - passed);
+ (data->state.resume_from - passed >
+ (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1,
+ data->state.fread_func(scratch, 1,
readthisamountnow, data->state.in);
passed += actuallyread;
@@ -1370,7 +1361,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh sftp send function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_OUT;
/* since we don't really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short
@@ -1560,7 +1551,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->readdir_longentry = NULL;
state(data, SSH_SFTP_READDIR_BOTTOM);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_SFTP_READDIR_BOTTOM:
if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
result = CURLE_OUT_OF_MEMORY;
@@ -1740,7 +1731,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh recv function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
if(result) {
/* this should never occur; the close state should be entered
@@ -1868,7 +1859,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh scp send function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_OUT;
state(data, SSH_STOP);
@@ -1884,7 +1875,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
state(data, SSH_SCP_DOWNLOAD);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_SCP_DOWNLOAD:{
curl_off_t bytecount;
@@ -1908,7 +1899,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh recv function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
state(data, SSH_STOP);
break;
@@ -1948,7 +1939,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
ssh_set_blocking(sshc->ssh_session, 0);
state(data, SSH_SESSION_DISCONNECT);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_SESSION_DISCONNECT:
/* during weird times when we've been prematurely aborted, the channel
@@ -1971,7 +1962,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
data->state.most_recent_ftp_entrypath = NULL;
state(data, SSH_SESSION_FREE);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_SESSION_FREE:
if(sshc->ssh_session) {
ssh_free(sshc->ssh_session);
@@ -2022,7 +2013,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
case SSH_QUIT:
- /* fallthrough, just stop! */
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
@@ -2613,7 +2603,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
return -1;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 1:
conn->proto.sshc.sftp_recv_state = 1;
@@ -2957,10 +2947,4 @@ void Curl_ssh_version(char *buffer, size_t buflen)
(void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
}
-#if defined(__GNUC__) && \
- (LIBSSH_VERSION_MINOR >= 10) || \
- (LIBSSH_VERSION_MAJOR > 0)
-#pragma GCC diagnostic pop
-#endif
-
#endif /* USE_LIBSSH */
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c
index 430f4e2ef3..32c6764be9 100644
--- a/libs/libcurl/src/vssh/libssh2.c
+++ b/libs/libcurl/src/vssh/libssh2.c
@@ -138,7 +138,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* domore_getsock */
ssh_getsock, /* perform_getsock */
scp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
PORT_SSH, /* defport */
@@ -167,7 +167,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* domore_getsock */
ssh_getsock, /* perform_getsock */
sftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
PORT_SSH, /* defport */
@@ -589,10 +589,9 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
switch(rc) {
default: /* unknown return codes will equal reject */
- /* FALLTHROUGH */
case CURLKHSTAT_REJECT:
state(data, SSH_SESSION_FREE);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURLKHSTAT_DEFER:
/* DEFER means bail out but keep the SSH_HOSTKEY state */
result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
@@ -601,9 +600,8 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
/* remove old host+key that doesn't match */
if(host)
libssh2_knownhost_del(sshc->kh, host);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURLKHSTAT_FINE:
- /* FALLTHROUGH */
case CURLKHSTAT_FINE_ADD_TO_FILE:
/* proceed */
if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
@@ -997,7 +995,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
}
state(data, SSH_S_STARTUP);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_S_STARTUP:
rc = session_startup(sshc->ssh_session, sock);
@@ -1016,7 +1014,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
state(data, SSH_HOSTKEY);
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSH_HOSTKEY:
/*
* Before we authenticate we should check the hostkey's fingerprint
@@ -1961,22 +1959,22 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(rc == 0) {
#ifdef _MSC_VER
- #define LIBSSH2_VFS_SIZE_MASK "I64u"
+ #define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
#else
- #define LIBSSH2_VFS_SIZE_MASK "llu"
+ #define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
#endif
char *tmp = aprintf("statvfs:\n"
- "f_bsize: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_frsize: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_blocks: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bfree: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bavail: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_files: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_ffree: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_favail: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_fsid: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_flag: %" LIBSSH2_VFS_SIZE_MASK "\n"
- "f_namemax: %" LIBSSH2_VFS_SIZE_MASK "\n",
+ "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
statvfs.f_bsize, statvfs.f_frsize,
statvfs.f_blocks, statvfs.f_bfree,
statvfs.f_bavail, statvfs.f_files,
@@ -2160,14 +2158,15 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
- curlx_sotouz(data->state.resume_from - passed);
+ (data->state.resume_from - passed >
+ (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread;
Curl_set_in_callback(data, true);
- actuallyread = data->state.fread_func(data->state.buffer, 1,
+ actuallyread = data->state.fread_func(scratch, 1,
readthisamountnow,
data->state.in);
Curl_set_in_callback(data, false);
@@ -2213,7 +2212,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 sftp send function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_OUT;
/* since we don't really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short
@@ -2602,7 +2601,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
if(result) {
/* this should never occur; the close state should be entered
@@ -2757,7 +2756,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 scp send function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_OUT;
state(data, SSH_STOP);
}
@@ -2819,7 +2818,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
if(result) {
state(data, SSH_SCP_CHANNEL_FREE);
@@ -3024,7 +3023,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
break;
case SSH_QUIT:
- /* fallthrough, just stop! */
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
@@ -3293,6 +3291,27 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
#ifndef CURL_DISABLE_PROXY
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
/*
+ Setup libssh2 callbacks to make it read/write TLS from the socket.
+
+ ssize_t
+ recvcb(libssh2_socket_t sock, void *buffer, size_t length,
+ int flags, void **abstract);
+
+ ssize_t
+ sendcb(libssh2_socket_t sock, const void *buffer, size_t length,
+ int flags, void **abstract);
+
+ */
+#if LIBSSH2_VERSION_NUM >= 0x010b01
+ infof(data, "Uses HTTPS proxy");
+ libssh2_session_callback_set2(sshc->ssh_session,
+ LIBSSH2_CALLBACK_RECV,
+ (libssh2_cb_generic *)ssh_tls_recv);
+ libssh2_session_callback_set2(sshc->ssh_session,
+ LIBSSH2_CALLBACK_SEND,
+ (libssh2_cb_generic *)ssh_tls_send);
+#else
+ /*
* This crazy union dance is here to avoid assigning a void pointer a
* function pointer as it is invalid C. The problem is of course that
* libssh2 has such an API...
@@ -3312,22 +3331,11 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
sshsend.sendptr = ssh_tls_send;
infof(data, "Uses HTTPS proxy");
- /*
- Setup libssh2 callbacks to make it read/write TLS from the socket.
-
- ssize_t
- recvcb(libssh2_socket_t sock, void *buffer, size_t length,
- int flags, void **abstract);
-
- ssize_t
- sendcb(libssh2_socket_t sock, const void *buffer, size_t length,
- int flags, void **abstract);
-
- */
libssh2_session_callback_set(sshc->ssh_session,
LIBSSH2_CALLBACK_RECV, sshrecv.recvp);
libssh2_session_callback_set(sshc->ssh_session,
LIBSSH2_CALLBACK_SEND, sshsend.sendp);
+#endif
/* Store the underlying TLS recv/send function pointers to be used when
reading from the proxy */
diff --git a/libs/libcurl/src/vssh/wolfssh.c b/libs/libcurl/src/vssh/wolfssh.c
index 346d1875b0..5583398915 100644
--- a/libs/libcurl/src/vssh/wolfssh.c
+++ b/libs/libcurl/src/vssh/wolfssh.c
@@ -42,6 +42,7 @@
#include "select.h"
#include "multiif.h"
#include "warnless.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -92,7 +93,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* domore_getsock */
wssh_getsock, /* perform_getsock */
wscp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
@@ -121,7 +122,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* domore_getsock */
wssh_getsock, /* perform_getsock */
wsftp_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_SSH, /* defport */
@@ -512,15 +513,9 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
return CURLE_OK;
}
else if(name && (rc == WS_SUCCESS)) {
- sshc->homedir = malloc(name->fSz + 1);
- if(!sshc->homedir) {
+ sshc->homedir = Curl_memdup0(name->fName, name->fSz);
+ if(!sshc->homedir)
sshc->actualcode = CURLE_OUT_OF_MEMORY;
- }
- else {
- memcpy(sshc->homedir, name->fName, name->fSz);
- sshc->homedir[name->fSz] = 0;
- infof(data, "wolfssh SFTP realpath succeeded");
- }
wolfSSH_SFTPNAME_list_free(name);
state(data, SSH_STOP);
return CURLE_OK;
@@ -646,14 +641,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
- curlx_sotouz(data->state.resume_from - passed);
+ (data->state.resume_from - passed >
+ (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread;
Curl_set_in_callback(data, true);
- actuallyread = data->state.fread_func(data->state.buffer, 1,
+ actuallyread = data->state.fread_func(scratch, 1,
readthisamountnow,
data->state.in);
Curl_set_in_callback(data, false);
@@ -699,7 +695,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 sftp send function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_OUT;
+ data->state.select_bits = CURL_CSELECT_OUT;
/* since we don't really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short
@@ -795,7 +791,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
- conn->cselect_bits = CURL_CSELECT_IN;
+ data->state.select_bits = CURL_CSELECT_IN;
if(result) {
/* this should never occur; the close state should be entered
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c
index 3bda30328c..924802c61f 100644
--- a/libs/libcurl/src/vtls/bearssl.c
+++ b/libs/libcurl/src/vtls/bearssl.c
@@ -509,7 +509,6 @@ static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
{
uint16_t selected_ciphers[NUM_OF_CIPHERS];
size_t selected_count = 0;
- char cipher_name[CIPHER_NAME_BUF_LEN];
const char *cipher_start = ciphers;
const char *cipher_end;
size_t i, j;
@@ -518,41 +517,48 @@ static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
return CURLE_SSL_CIPHER;
while(true) {
+ const char *cipher;
+ size_t clen;
+
/* Extract the next cipher name from the ciphers string */
while(is_separator(*cipher_start))
++cipher_start;
- if(*cipher_start == '\0')
+ if(!*cipher_start)
break;
cipher_end = cipher_start;
- while(*cipher_end != '\0' && !is_separator(*cipher_end))
+ while(*cipher_end && !is_separator(*cipher_end))
++cipher_end;
- j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ?
- cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1;
- strncpy(cipher_name, cipher_start, j);
- cipher_name[j] = '\0';
+
+ clen = cipher_end - cipher_start;
+ cipher = cipher_start;
+
cipher_start = cipher_end;
/* Lookup the cipher name in the table of available ciphers. If the cipher
name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try
to match cipher name by an (OpenSSL) alias. */
- if(strncasecompare(cipher_name, "TLS_", 4)) {
+ if(strncasecompare(cipher, "TLS_", 4)) {
for(i = 0; i < NUM_OF_CIPHERS &&
- !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+ (strlen(ciphertable[i].name) == clen) &&
+ !strncasecompare(cipher, ciphertable[i].name, clen); ++i);
}
else {
for(i = 0; i < NUM_OF_CIPHERS &&
- !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+ (strlen(ciphertable[i].alias_name) == clen) &&
+ !strncasecompare(cipher, ciphertable[i].alias_name, clen); ++i);
}
if(i == NUM_OF_CIPHERS) {
- infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+ infof(data, "BearSSL: unknown cipher in list: %.*s",
+ (int)clen, cipher);
continue;
}
/* No duplicates allowed */
for(j = 0; j < selected_count &&
- selected_ciphers[j] != ciphertable[i].num; j++);
+ selected_ciphers[j] != ciphertable[i].num; j++);
if(j < selected_count) {
- infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+ infof(data, "BearSSL: duplicate cipher in list: %.*s",
+ (int)clen, cipher);
continue;
}
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c
index bd78eb5030..dd40380738 100644
--- a/libs/libcurl/src/vtls/gtls.c
+++ b/libs/libcurl/src/vtls/gtls.c
@@ -584,13 +584,9 @@ CURLcode gtls_client_init(struct Curl_easy *data,
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
if(config->username) {
- size_t len = strlen(prioritylist);
-
- char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
+ char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
if(!prioritysrp)
return CURLE_OUT_OF_MEMORY;
- strcpy(prioritysrp, prioritylist);
- strcpy(prioritysrp + len, ":" GNUTLS_SRP);
rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
free(prioritysrp);
@@ -822,16 +818,17 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
char certname[65] = ""; /* limited to 64 chars by ASN.1 */
size_t size;
time_t certclock;
- const char *ptr;
int rc;
CURLcode result = CURLE_OK;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ const char *ptr;
unsigned int algo;
unsigned int bits;
gnutls_protocol_t version = gnutls_protocol_get_version(session);
#endif
long * const certverifyresult = &ssl_config->certverifyresult;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
/* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
gnutls_cipher_get(session),
@@ -839,6 +836,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
infof(data, "SSL connection using %s / %s",
gnutls_protocol_get_name(version), ptr);
+#endif
/* This function will return the peer's raw certificate (chain) as sent by
the peer. These certificates are in raw format (DER encoded for
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index 292c933291..c008eace9b 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -36,6 +36,13 @@
/* Define this to enable lots of debugging for mbedTLS */
/* #define MBEDTLS_DEBUG */
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+/* mbedTLS (as of v3.5.1) has a duplicate function declaration
+ in its public headers. Disable the warning that detects it. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x02040000
#include <mbedtls/net_sockets.h>
@@ -56,6 +63,10 @@
# endif
#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
#include "urldata.h"
#include "sendf.h"
#include "inet_pton.h"
@@ -67,6 +78,7 @@
#include "select.h"
#include "multiif.h"
#include "mbedtls_threadlock.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -153,7 +165,6 @@ static void mbed_debug(void *context, int level, const char *f_name,
infof(data, "%s", line);
(void) level;
}
-#else
#endif
static int mbedtls_bio_cf_write(void *bio,
@@ -165,6 +176,9 @@ static int mbedtls_bio_cf_write(void *bio,
CURLcode result;
DEBUGASSERT(data);
+ if(!data)
+ return 0;
+
nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
@@ -182,6 +196,8 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen)
CURLcode result;
DEBUGASSERT(data);
+ if(!data)
+ return 0;
/* OpenSSL catches this case, so should we. */
if(!buf)
return 0;
@@ -367,11 +383,10 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
terminated even when provided the exact length, forcing us to waste
extra memory here. */
- unsigned char *newblob = malloc(ca_info_blob->len + 1);
+ unsigned char *newblob = Curl_memdup0(ca_info_blob->data,
+ ca_info_blob->len);
if(!newblob)
return CURLE_OUT_OF_MEMORY;
- memcpy(newblob, ca_info_blob->data, ca_info_blob->len);
- newblob[ca_info_blob->len] = 0; /* null terminate */
ret = mbedtls_x509_crt_parse(&backend->cacert, newblob,
ca_info_blob->len + 1);
free(newblob);
@@ -441,11 +456,10 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
terminated even when provided the exact length, forcing us to waste
extra memory here. */
- unsigned char *newblob = malloc(ssl_cert_blob->len + 1);
+ unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data,
+ ssl_cert_blob->len);
if(!newblob)
return CURLE_OUT_OF_MEMORY;
- memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len);
- newblob[ssl_cert_blob->len] = 0; /* null terminate */
ret = mbedtls_x509_crt_parse(&backend->clicert, newblob,
ssl_cert_blob->len + 1);
free(newblob);
@@ -1207,6 +1221,9 @@ static int mbedtls_init(void)
static void mbedtls_cleanup(void)
{
+#ifdef THREADING_SUPPORT
+ mbedtls_entropy_free(&ts_entropy);
+#endif /* THREADING_SUPPORT */
(void)Curl_mbedtlsthreadlock_thread_cleanup();
}
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index fcc4e4eb0b..be6780b7ed 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -954,8 +954,9 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
#endif
if(!*buf) {
- strncpy(buf, (error ? "Unknown error" : "No error"), size);
- buf[size - 1] = '\0';
+ const char *msg = error ? "Unknown error" : "No error";
+ if(strlen(msg) < size)
+ strcpy(buf, msg);
}
return buf;
@@ -1087,6 +1088,7 @@ static int ssl_ui_reader(UI *ui, UI_STRING *uis)
UI_set_result(ui, uis, password);
return 1;
}
+ FALLTHROUGH();
default:
break;
}
@@ -1105,6 +1107,7 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis)
(UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
return 1;
}
+ FALLTHROUGH();
default:
break;
}
@@ -1522,7 +1525,7 @@ fail:
case SSL_FILETYPE_PEM:
if(cert_done)
break;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSL_FILETYPE_ASN1:
cert_use_result = key_blob ?
SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
@@ -1752,7 +1755,7 @@ static int ossl_init(void)
static void ossl_cleanup(void)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- !defined(LIBRESSL_VERSION_NUMBER)
+ (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
/* OpenSSL 1.1 deprecates all these cleanup functions and
turns them into no-ops in OpenSSL 1.0 compatibility mode */
#else
@@ -2848,7 +2851,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_2:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
@@ -2856,7 +2859,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_1:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1;
@@ -2864,7 +2867,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1:
break;
@@ -2875,12 +2878,12 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_1:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_2;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_2:
#ifdef TLS1_3_VERSION
*ctx_options |= SSL_OP_NO_TLSv1_3;
@@ -3174,6 +3177,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
bool imported_native_ca = false;
bool imported_ca_info_blob = false;
+ CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ ssl_cafile? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
@@ -4346,6 +4351,20 @@ static CURLcode servercert(struct Curl_cfilter *cf,
/* don't do this after Session ID reuse */
result = verifystatus(cf, data);
if(result) {
+ /* when verifystatus failed, remove the session id from the cache again
+ if present */
+ if(!Curl_ssl_cf_is_proxy(cf)) {
+ void *old_ssl_sessionid = NULL;
+ bool incache;
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ if(incache) {
+ infof(data, "Remove session ID again from cache");
+ Curl_ssl_delsessionid(data, old_ssl_sessionid);
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
X509_free(backend->server_cert);
backend->server_cert = NULL;
return result;
@@ -4592,10 +4611,10 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
else if(sockerr)
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
- else {
- strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
- error_buffer[sizeof(error_buffer) - 1] = '\0';
- }
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
+
failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_SEND_ERROR;
@@ -4688,10 +4707,9 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
else if(sockerr && err == SSL_ERROR_SYSCALL)
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
- else {
- strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
- error_buffer[sizeof(error_buffer) - 1] = '\0';
- }
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_RECV_ERROR;
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c
index d30325d098..f0eb1443e6 100644
--- a/libs/libcurl/src/vtls/rustls.c
+++ b/libs/libcurl/src/vtls/rustls.c
@@ -156,7 +156,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
size_t errorlen;
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
failf(data, "rustls_connection_process_new_packets: %.*s",
- errorlen, errorbuf);
+ (int)errorlen, errorbuf);
*err = map_error(rresult);
return -1;
}
@@ -225,7 +225,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char errorbuf[255];
size_t errorlen;
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
*err = CURLE_READ_ERROR;
nread = -1;
goto out;
@@ -301,7 +301,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
&plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -459,7 +459,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(result != RUSTLS_RESULT_OK) {
rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
return CURLE_COULDNT_CONNECT;
}
rustls_connection_set_userdata(rconn, backend);
@@ -563,8 +563,8 @@ cr_connect_common(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
if(blocking && 0 == what) {
- failf(data, "rustls connection timeout after %d ms",
- socket_check_timeout);
+ failf(data, "rustls connection timeout after %"
+ CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
return CURLE_OPERATION_TIMEDOUT;
}
if(0 == what) {
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c
index c04f968f29..64a31f155d 100644
--- a/libs/libcurl/src/vtls/schannel.c
+++ b/libs/libcurl/src/vtls/schannel.c
@@ -439,6 +439,12 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
return CURLE_OK;
}
#endif
+
+static bool algo(const char *check, char *namep, size_t nlen)
+{
+ return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
+}
+
static CURLcode
schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct Curl_easy *data)
@@ -660,7 +666,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
cert_showfilename_error);
else
failf(data, "schannel: Failed to import cert file %s, "
- "last error is 0x%x",
+ "last error is 0x%lx",
cert_showfilename_error, errorcode);
return CURLE_SSL_CERTPROBLEM;
}
@@ -671,7 +677,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
if(!client_certs[0]) {
failf(data, "schannel: Failed to get certificate from file %s"
- ", last error is 0x%x",
+ ", last error is 0x%lx",
cert_showfilename_error, GetLastError());
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
@@ -684,10 +690,15 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
cert_store_path);
if(!cert_store) {
- failf(data, "schannel: Failed to open cert store %x %s, "
- "last error is 0x%x",
- cert_store_name, cert_store_path, GetLastError());
+ char *path_utf8 =
+ curlx_convert_tchar_to_UTF8(cert_store_path);
+ failf(data, "schannel: Failed to open cert store %lx %s, "
+ "last error is 0x%lx",
+ cert_store_name,
+ (path_utf8 ? path_utf8 : "(unknown)"),
+ GetLastError());
free(cert_store_path);
+ curlx_unicodefree(path_utf8);
curlx_unicodefree(cert_path);
return CURLE_SSL_CERTPROBLEM;
}
@@ -790,9 +801,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
char *startCur = ciphers13;
int algCount = 0;
- char tmp[LONGEST_ALG_ID] = { 0 };
char *nameEnd;
- size_t n;
disable_aes_gcm_sha384 = TRUE;
disable_aes_gcm_sha256 = TRUE;
@@ -801,40 +810,34 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
disable_aes_ccm_sha256 = TRUE;
while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+ size_t n;
+ char *namep;
nameEnd = strchr(startCur, ':');
n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+ namep = startCur;
- /* reject too-long cipher names */
- if(n > (LONGEST_ALG_ID - 1)) {
- failf(data, "schannel: Cipher name too long, not checked");
- return CURLE_SSL_CIPHER;
- }
-
- strncpy(tmp, startCur, n);
- tmp[n] = 0;
-
- if(disable_aes_gcm_sha384
- && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) {
+ if(disable_aes_gcm_sha384 &&
+ algo("TLS_AES_256_GCM_SHA384", namep, n)) {
disable_aes_gcm_sha384 = FALSE;
}
else if(disable_aes_gcm_sha256
- && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+ && algo("TLS_AES_128_GCM_SHA256", namep, n)) {
disable_aes_gcm_sha256 = FALSE;
}
else if(disable_chacha_poly
- && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+ && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
disable_chacha_poly = FALSE;
}
else if(disable_aes_ccm_8_sha256
- && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+ && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
disable_aes_ccm_8_sha256 = FALSE;
}
else if(disable_aes_ccm_sha256
- && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+ && algo("TLS_AES_128_CCM_SHA256", namep, n)) {
disable_aes_ccm_sha256 = FALSE;
}
else {
- failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp);
+ failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
return CURLE_SSL_CIPHER;
}
@@ -1195,9 +1198,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
cur += proto.len;
*list_len = curlx_uitous(cur - list_start_index);
- *extension_len = *list_len +
- (unsigned short)sizeof(unsigned int) +
- (unsigned short)sizeof(unsigned short);
+ *extension_len = (unsigned int)(*list_len +
+ sizeof(unsigned int) + sizeof(unsigned short));
InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
@@ -2332,10 +2334,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
else {
#ifndef CURL_DISABLE_VERBOSE_STRINGS
char buffer[STRERROR_LEN];
-#endif
- *err = CURLE_RECV_ERROR;
infof(data, "schannel: failed to read data from server: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+#endif
+ *err = CURLE_RECV_ERROR;
goto cleanup;
}
}
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c
index 0c9c00c16a..743f3b059e 100644
--- a/libs/libcurl/src/vtls/schannel_verify.c
+++ b/libs/libcurl/src/vtls/schannel_verify.c
@@ -172,7 +172,7 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
/* Sanity check that the cert_context object is the right type */
if(CERT_QUERY_CONTENT_CERT != actual_content_type) {
failf(data,
- "schannel: unexpected content type '%d' when extracting "
+ "schannel: unexpected content type '%lu' when extracting "
"certificate from CA file '%s'",
actual_content_type, ca_file_text);
result = CURLE_SSL_CACERT_BADFILE;
@@ -753,7 +753,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
failf(data, "schannel: CertGetCertificateChain trust error"
" CERT_TRUST_REVOCATION_STATUS_UNKNOWN");
else
- failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+ failf(data, "schannel: CertGetCertificateChain error mask: 0x%08lx",
dwTrustErrorMask);
result = CURLE_PEER_FAILED_VERIFICATION;
}
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c
index 26b673016e..eb43d08c5b 100644
--- a/libs/libcurl/src/vtls/sectransp.c
+++ b/libs/libcurl/src/vtls/sectransp.c
@@ -906,7 +906,6 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
return rtn;
}
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
{
/* The first ciphers in the ciphertable are continuous. Here we do small
@@ -925,7 +924,6 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
}
return ciphertable[SSL_NULL_WITH_NULL_NULL].name;
}
-#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
#if CURL_BUILD_MAC
CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
@@ -2369,19 +2367,15 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
const struct curl_blob *ca_info_blob,
SSLContextRef ctx)
{
- int result;
+ CURLcode result;
unsigned char *certbuf;
size_t buflen;
+ bool free_certbuf = FALSE;
if(ca_info_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;
- }
+ certbuf = ca_info_blob->data;
buflen = ca_info_blob->len;
- memcpy(certbuf, ca_info_blob->data, ca_info_blob->len);
- certbuf[ca_info_blob->len]='\0';
}
else if(cafile) {
CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile);
@@ -2389,12 +2383,14 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
failf(data, "SSL: failed to read or invalid CA certificate");
return CURLE_SSL_CACERT_BADFILE;
}
+ free_certbuf = TRUE;
}
else
return CURLE_SSL_CACERT_BADFILE;
result = verify_cert_buf(cf, data, certbuf, buflen, ctx);
- free(certbuf);
+ if(free_certbuf)
+ free(certbuf);
return result;
}
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c
index f37907c494..6deb512879 100644
--- a/libs/libcurl/src/vtls/vtls.c
+++ b/libs/libcurl/src/vtls/vtls.c
@@ -883,28 +883,21 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
size_t valuelen)
{
struct curl_certinfo *ci = &data->info.certs;
- char *output;
struct curl_slist *nl;
CURLcode result = CURLE_OK;
- size_t labellen = strlen(label);
- size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
+ struct dynbuf build;
- output = malloc(outlen);
- if(!output)
- return CURLE_OUT_OF_MEMORY;
-
- /* sprintf the label and colon */
- msnprintf(output, outlen, "%s:", label);
+ Curl_dyn_init(&build, 10000);
- /* memcpy the value (it might not be null-terminated) */
- memcpy(&output[labellen + 1], value, valuelen);
-
- /* null-terminate the output */
- output[labellen + 1 + valuelen] = 0;
+ if(Curl_dyn_add(&build, label) ||
+ Curl_dyn_addn(&build, ":", 1) ||
+ Curl_dyn_addn(&build, value, valuelen))
+ return CURLE_OUT_OF_MEMORY;
- nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
+ nl = Curl_slist_append_nodup(ci->certinfo[certnum],
+ Curl_dyn_ptr(&build));
if(!nl) {
- free(output);
+ Curl_dyn_free(&build);
curl_slist_free_all(ci->certinfo[certnum]);
result = CURLE_OUT_OF_MEMORY;
}
@@ -1002,7 +995,7 @@ 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 = 0, pinkeylen;
+ size_t encodedlen = 0;
char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos;
unsigned char *sha256sumdigest;
@@ -1030,13 +1023,11 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
infof(data, " public key hash: sha256//%s", encoded);
/* it starts with sha256//, copy so we can modify it */
- pinkeylen = strlen(pinnedpubkey) + 1;
- pinkeycopy = malloc(pinkeylen);
+ pinkeycopy = strdup(pinnedpubkey);
if(!pinkeycopy) {
Curl_safefree(encoded);
return CURLE_OUT_OF_MEMORY;
}
- memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
/* point begin_pos to the copy, and start extracting keys */
begin_pos = pinkeycopy;
do {
@@ -1422,17 +1413,13 @@ static size_t multissl_version(char *buffer, size_t size)
backends_len = p - backends;
}
- if(!size)
- return 0;
-
- if(size <= backends_len) {
- strncpy(buffer, backends, size - 1);
- buffer[size - 1] = '\0';
- return size - 1;
+ if(size) {
+ if(backends_len < size)
+ strcpy(buffer, backends);
+ else
+ *buffer = 0; /* did not fit */
}
-
- strcpy(buffer, backends);
- return backends_len;
+ return 0;
}
static int multissl_setup(const struct Curl_ssl *backend)
@@ -1728,18 +1715,34 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
{
struct cf_call_data save;
ssize_t nread;
+ size_t ntotal = 0;
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);
- }
- else if(nread == 0) {
- /* eof */
+ /* Do receive until we fill the buffer somehwhat or EGAIN, error or EOF */
+ while(!ntotal || (len - ntotal) > (4*1024)) {
*err = CURLE_OK;
+ nread = Curl_ssl->recv_plain(cf, data, buf + ntotal, len - ntotal, err);
+ if(nread < 0) {
+ if(*err == CURLE_AGAIN && ntotal > 0) {
+ /* we EAGAINed after having reed data, return the success amount */
+ *err = CURLE_OK;
+ break;
+ }
+ /* we have a an error to report */
+ goto out;
+ }
+ else if(nread == 0) {
+ /* eof */
+ break;
+ }
+ ntotal += (size_t)nread;
+ DEBUGASSERT((size_t)ntotal <= len);
}
- CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err);
+ nread = (ssize_t)ntotal;
+out:
+ CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
+ nread, *err);
CF_DATA_RESTORE(cf, save);
return nread;
}
@@ -1855,6 +1858,8 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_query,
};
+#ifndef CURL_DISABLE_PROXY
+
struct Curl_cftype Curl_cft_ssl_proxy = {
"SSL-PROXY",
CF_TYPE_SSL,
@@ -1873,6 +1878,8 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
Curl_cf_def_query,
};
+#endif /* !CURL_DISABLE_PROXY */
+
static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn)
@@ -1980,8 +1987,12 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option)
static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
- if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy)
+ if(cf->cft == &Curl_cft_ssl)
+ return cf;
+#ifndef CURL_DISABLE_PROXY
+ if(cf->cft == &Curl_cft_ssl_proxy)
return cf;
+#endif
}
return NULL;
}
@@ -2027,7 +2038,12 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
{
+#ifndef CURL_DISABLE_PROXY
return (cf->cft == &Curl_cft_ssl_proxy);
+#else
+ (void)cf;
+ return FALSE;
+#endif
}
struct ssl_config_data *
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h
index 111d17f32c..0bfefd0502 100644
--- a/libs/libcurl/src/vtls/vtls.h
+++ b/libs/libcurl/src/vtls/vtls.h
@@ -228,7 +228,9 @@ struct ssl_primary_config *
Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
extern struct Curl_cftype Curl_cft_ssl;
+#ifndef CURL_DISABLE_PROXY
extern struct Curl_cftype Curl_cft_ssl_proxy;
+#endif
#else /* if not USE_SSL */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c
index 32681520a0..349d077ec4 100644
--- a/libs/libcurl/src/vtls/wolfssl.c
+++ b/libs/libcurl/src/vtls/wolfssl.c
@@ -583,12 +583,25 @@ 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(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;
+ if(file_type == WOLFSSL_FILETYPE_PEM) {
+ if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx,
+ ssl_config->primary.clientcert)
+ != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else if(file_type == WOLFSSL_FILETYPE_ASN1) {
+ if(wolfSSL_CTX_use_certificate_file(backend->ctx,
+ ssl_config->primary.clientcert,
+ file_type) != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+ failf(data, "unknown cert type");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
file_type = do_file_type(ssl_config->key_type);
@@ -1084,9 +1097,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
*curlcode = CURLE_OK;
return 0;
case SSL_ERROR_NONE:
- /* FALLTHROUGH */
case SSL_ERROR_WANT_READ:
- /* FALLTHROUGH */
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke wolfSSL_read() */
CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c
index 0ce971c7ba..50a5fb5e9f 100644
--- a/libs/libcurl/src/vtls/x509asn1.c
+++ b/libs/libcurl/src/vtls/x509asn1.c
@@ -97,6 +97,11 @@
#define CURL_ASN1_CHARACTER_STRING 29
#define CURL_ASN1_BMP_STRING 30
+/* Max sixes */
+
+#define MAX_X509_STR 10000
+#define MAX_X509_CERT 100000
+
#ifdef WANT_EXTRACT_CERTINFO
/* ASN.1 OID table entry. */
struct Curl_OID {
@@ -255,61 +260,61 @@ static const struct Curl_OID *searchOID(const char *oid)
}
/*
- * Convert an ASN.1 Boolean value into its string representation. Return the
- * dynamically allocated string, or NULL if source is not an ASN.1 Boolean
- * value.
+ * Convert an ASN.1 Boolean value into its string representation.
+ *
+ * Return error code.
*/
-static const char *bool2str(const char *beg, const char *end)
+static CURLcode bool2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
if(end - beg != 1)
- return NULL;
- return strdup(*beg? "TRUE": "FALSE");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return Curl_dyn_add(store, *beg? "TRUE": "FALSE");
}
/*
* Convert an ASN.1 octet string to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
*/
-static const char *octet2str(const char *beg, const char *end)
+static CURLcode octet2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
- struct dynbuf buf;
- CURLcode result;
-
- Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1);
- result = Curl_dyn_addn(&buf, "", 0);
+ CURLcode result = CURLE_OK;
while(!result && beg < end)
- result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+ result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
- return Curl_dyn_ptr(&buf);
+ return result;
}
-static const char *bit2str(const char *beg, const char *end)
+static CURLcode bit2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
- /* Convert an ASN.1 bit string to a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
+ /* Convert an ASN.1 bit string to a printable string. */
if(++beg > end)
- return NULL;
- return octet2str(beg, end);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return octet2str(store, beg, end);
}
/*
* Convert an ASN.1 integer value into its string representation.
- * Return the dynamically allocated string, or NULL if source is not an
- * ASN.1 integer value.
+ *
+ * Returns error.
*/
-static const char *int2str(const char *beg, const char *end)
+static CURLcode int2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
unsigned int val = 0;
size_t n = end - beg;
if(!n)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(n > 4)
- return octet2str(beg, end);
+ return octet2str(store, beg, end);
/* Represent integers <= 32-bit as a single value. */
if(*beg & 0x80)
@@ -318,25 +323,24 @@ static const char *int2str(const char *beg, const char *end)
do
val = (val << 8) | *(const unsigned char *) beg++;
while(beg < end);
- return curl_maprintf("%s%x", val >= 10? "0x": "", val);
+ return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val);
}
/*
- * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
- * destination buffer dynamically. The allocation size will normally be too
- * large: this is to avoid buffer overflows.
- * Terminate the string with a nul byte and return the converted
- * string length.
+ * Convert from an ASN.1 typed string to UTF8.
+ *
+ * The result is stored in a dynbuf that is inited by the user of this
+ * function.
+ *
+ * Returns error.
*/
-static ssize_t
-utf8asn1str(char **to, int type, const char *from, const char *end)
+static CURLcode
+utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
{
size_t inlength = end - from;
int size = 1;
- size_t outlength;
- char *buf;
+ CURLcode result = CURLE_OK;
- *to = NULL;
switch(type) {
case CURL_ASN1_BMP_STRING:
size = 2;
@@ -352,133 +356,85 @@ utf8asn1str(char **to, int type, const char *from, const char *end)
case CURL_ASN1_UTF8_STRING:
break;
default:
- return -1; /* Conversion not supported. */
+ return CURLE_BAD_FUNCTION_ARGUMENT; /* Conversion not supported. */
}
if(inlength % size)
- return -1; /* Length inconsistent with character size. */
- if(inlength / size > (SIZE_T_MAX - 1) / 4)
- return -1; /* Too big. */
- buf = malloc(4 * (inlength / size) + 1);
- if(!buf)
- return -1; /* Not enough memory. */
+ /* Length inconsistent with character size. */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(type == CURL_ASN1_UTF8_STRING) {
/* Just copy. */
- outlength = inlength;
- if(outlength)
- memcpy(buf, from, outlength);
+ if(inlength)
+ result = Curl_dyn_addn(to, from, inlength);
}
else {
- for(outlength = 0; from < end;) {
- int charsize;
- unsigned int wc;
+ while(!result && (from < end)) {
+ char buf[4]; /* decode buffer */
+ int charsize = 1;
+ unsigned int wc = 0;
- wc = 0;
switch(size) {
case 4:
wc = (wc << 8) | *(const unsigned char *) from++;
wc = (wc << 8) | *(const unsigned char *) from++;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 2:
wc = (wc << 8) | *(const unsigned char *) from++;
- /* FALLTHROUGH */
+ FALLTHROUGH();
default: /* case 1: */
wc = (wc << 8) | *(const unsigned char *) from++;
}
- charsize = 1;
if(wc >= 0x00000080) {
if(wc >= 0x00000800) {
if(wc >= 0x00010000) {
if(wc >= 0x00200000) {
free(buf);
- return -1; /* Invalid char. size for target encoding. */
+ /* Invalid char. size for target encoding. */
+ return CURLE_WEIRD_SERVER_REPLY;
}
- buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+ buf[3] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x00010000;
charsize++;
}
- buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+ buf[2] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x00000800;
charsize++;
}
- buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+ buf[1] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x000000C0;
charsize++;
}
- buf[outlength] = (char) wc;
- outlength += charsize;
+ buf[0] = (char) wc;
+ result = Curl_dyn_addn(to, buf, charsize);
}
}
- buf[outlength] = '\0';
- *to = buf;
- return outlength;
-}
-
-/*
- * Convert an ASN.1 String into its UTF-8 string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
- */
-static const char *string2str(int type, const char *beg, const char *end)
-{
- char *buf;
- if(utf8asn1str(&buf, type, beg, end) < 0)
- return NULL;
- return buf;
-}
-
-/*
- * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at
- * buf. Return the total number of encoded digits, even if larger than
- * `buflen'.
- */
-static size_t encodeUint(char *buf, size_t buflen, unsigned int x)
-{
- size_t i = 0;
- unsigned int y = x / 10;
-
- if(y) {
- i = encodeUint(buf, buflen, y);
- x -= y * 10;
- }
- if(i < buflen)
- buf[i] = (char) ('0' + x);
- i++;
- if(i < buflen)
- buf[i] = '\0'; /* Store a terminator if possible. */
- return i;
+ return result;
}
/*
* Convert an ASN.1 OID into its dotted string representation.
- * Store the result in th `n'-byte buffer at `buf'.
- * Return the converted string length, or 0 on errors.
+ *
+ * Return error code.
*/
-static size_t encodeOID(char *buf, size_t buflen,
- const char *beg, const char *end)
+static CURLcode encodeOID(struct dynbuf *store,
+ const char *beg, const char *end)
{
- size_t i;
unsigned int x;
unsigned int y;
+ CURLcode result = CURLE_OK;
/* Process the first two numbers. */
y = *(const unsigned char *) beg++;
x = y / 40;
y -= x * 40;
- i = encodeUint(buf, buflen, x);
- if(i < buflen)
- buf[i] = '.';
- i++;
- if(i >= buflen)
- i += encodeUint(NULL, 0, y);
- else
- i += encodeUint(buf + i, buflen - i, y);
+
+ result = Curl_dyn_addf(store, "%u.%u", x, y);
+ if(result)
+ return result;
/* Process the trailing numbers. */
while(beg < end) {
- if(i < buflen)
- buf[i] = '.';
- i++;
x = 0;
do {
if(x & 0xFF000000)
@@ -486,46 +442,42 @@ static size_t encodeOID(char *buf, size_t buflen,
y = *(const unsigned char *) beg++;
x = (x << 7) | (y & 0x7F);
} while(y & 0x80);
- if(i >= buflen)
- i += encodeUint(NULL, 0, x);
- else
- i += encodeUint(buf + i, buflen - i, x);
+ result = Curl_dyn_addf(store, ".%u", x);
}
- if(i < buflen)
- buf[i] = '\0';
- return i;
+ return result;
}
/*
* Convert an ASN.1 OID into its dotted or symbolic string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
*/
-static const char *OID2str(const char *beg, const char *end, bool symbolic)
+static CURLcode OID2str(struct dynbuf *store,
+ const char *beg, const char *end, bool symbolic)
{
- char *buf = NULL;
+ CURLcode result = CURLE_OK;
if(beg < end) {
- size_t buflen = encodeOID(NULL, 0, beg, end);
- if(buflen) {
- buf = malloc(buflen + 1); /* one extra for the zero byte */
- if(buf) {
- encodeOID(buf, buflen, beg, end);
- buf[buflen] = '\0';
-
- if(symbolic) {
- const struct Curl_OID *op = searchOID(buf);
- if(op) {
- free(buf);
- buf = strdup(op->textoid);
- }
- }
+ if(symbolic) {
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_X509_STR);
+ result = encodeOID(&buf, beg, end);
+
+ if(!result) {
+ const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+ if(op)
+ result = Curl_dyn_add(store, op->textoid);
+ Curl_dyn_free(&buf);
}
}
+ else
+ result = encodeOID(store, beg, end);
}
- return buf;
+ return result;
}
-static const char *GTime2str(const char *beg, const char *end)
+static CURLcode GTime2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
const char *tzp;
const char *fracp;
@@ -548,12 +500,12 @@ static const char *GTime2str(const char *beg, const char *end)
break;
case 2:
sec1 = fracp[-2];
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 1:
sec2 = fracp[-1];
break;
default:
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
/* Scan for timezone, measure fractional seconds. */
@@ -582,7 +534,8 @@ static const char *GTime2str(const char *beg, const char *end)
}
tzl = end - tzp;
- return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+ return Curl_dyn_addf(store,
+ "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
beg, beg + 4, beg + 6,
beg + 8, beg + 10, sec1, sec2,
fracl? ".": "", (int)fracl, fracp,
@@ -590,10 +543,12 @@ static const char *GTime2str(const char *beg, const char *end)
}
/*
- * Convert an ASN.1 UTC time to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Convert an ASN.1 UTC time to a printable string.
+ *
+ * Return error code.
*/
-static const char *UTime2str(const char *beg, const char *end)
+static CURLcode UTime2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
const char *tzp;
size_t tzl;
@@ -606,15 +561,16 @@ static const char *UTime2str(const char *beg, const char *end)
switch(tzp - sec) {
case 0:
sec = "00";
+ FALLTHROUGH();
case 2:
break;
default:
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
/* Process timezone. */
if(tzp >= end)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(*tzp == 'Z') {
tzp = "GMT";
end = tzp + 3;
@@ -623,7 +579,7 @@ static const char *UTime2str(const char *beg, const char *end)
tzp++;
tzl = end - tzp;
- return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
20 - (*beg >= '5'), beg, beg + 2, beg + 4,
beg + 6, beg + 8, sec,
(int)tzl, tzp);
@@ -631,34 +587,45 @@ static const char *UTime2str(const char *beg, const char *end)
/*
* Convert an ASN.1 element to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error
*/
-static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
+static CURLcode ASN1tostr(struct dynbuf *store,
+ struct Curl_asn1Element *elem, int type)
{
+ CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
if(elem->constructed)
- return NULL; /* No conversion of structured elements. */
+ return CURLE_OK; /* No conversion of structured elements. */
if(!type)
type = elem->tag; /* Type not forced: use element tag as type. */
switch(type) {
case CURL_ASN1_BOOLEAN:
- return bool2str(elem->beg, elem->end);
+ result = bool2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_INTEGER:
case CURL_ASN1_ENUMERATED:
- return int2str(elem->beg, elem->end);
+ result = int2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_BIT_STRING:
- return bit2str(elem->beg, elem->end);
+ result = bit2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_OCTET_STRING:
- return octet2str(elem->beg, elem->end);
+ result = octet2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_NULL:
- return strdup("");
+ result = Curl_dyn_addn(store, "", 1);
+ break;
case CURL_ASN1_OBJECT_IDENTIFIER:
- return OID2str(elem->beg, elem->end, TRUE);
+ result = OID2str(store, elem->beg, elem->end, TRUE);
+ break;
case CURL_ASN1_UTC_TIME:
- return UTime2str(elem->beg, elem->end);
+ result = UTime2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_GENERALIZED_TIME:
- return GTime2str(elem->beg, elem->end);
+ result = GTime2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_UTF8_STRING:
case CURL_ASN1_NUMERIC_STRING:
case CURL_ASN1_PRINTABLE_STRING:
@@ -667,87 +634,96 @@ static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
case CURL_ASN1_VISIBLE_STRING:
case CURL_ASN1_UNIVERSAL_STRING:
case CURL_ASN1_BMP_STRING:
- return string2str(type, elem->beg, elem->end);
+ result = utf8asn1str(store, type, elem->beg, elem->end);
+ break;
}
- return NULL; /* Unsupported. */
+ return result;
}
/*
- * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
- * `buf'.
+ * ASCII encode distinguished name at `dn' into the store dynbuf.
*
- * Returns the total string length, even if larger than `buflen' or -1 on
- * error.
+ * Returns error.
*/
-static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
+static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
{
struct Curl_asn1Element rdn;
struct Curl_asn1Element atv;
struct Curl_asn1Element oid;
struct Curl_asn1Element value;
- size_t l = 0;
const char *p1;
const char *p2;
const char *p3;
const char *str;
+ CURLcode result = CURLE_OK;
+ bool added = FALSE;
+ struct dynbuf temp;
+ Curl_dyn_init(&temp, MAX_X509_STR);
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
- if(!p1)
- return -1;
+ if(!p1) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
for(p2 = rdn.beg; p2 < rdn.end;) {
p2 = getASN1Element(&atv, p2, rdn.end);
- if(!p2)
- return -1;
+ if(!p2) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
p3 = getASN1Element(&oid, atv.beg, atv.end);
- if(!p3)
- return -1;
- if(!getASN1Element(&value, p3, atv.end))
- return -1;
- str = ASN1tostr(&oid, 0);
- if(!str)
- return -1;
+ if(!p3) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
+ if(!getASN1Element(&value, p3, atv.end)) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
+ Curl_dyn_reset(&temp);
+ result = ASN1tostr(&temp, &oid, 0);
+ if(result)
+ goto error;
+
+ str = Curl_dyn_ptr(&temp);
/* Encode delimiter.
If attribute has a short uppercase name, delimiter is ", ". */
- if(l) {
- for(p3 = str; ISUPPER(*p3); p3++)
- ;
- for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
+ for(p3 = str; ISUPPER(*p3); p3++)
+ ;
+ if(added) {
+ if(p3 - str > 2)
+ result = Curl_dyn_addn(store, "/", 1);
+ else
+ result = Curl_dyn_addn(store, ", ", 2);
+ if(result)
+ goto error;
}
/* Encode attribute name. */
- for(p3 = str; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
- free((char *) str);
+ result = Curl_dyn_add(store, str);
+ if(result)
+ goto error;
/* Generate equal sign. */
- if(l < buflen)
- buf[l] = '=';
- l++;
+ result = Curl_dyn_addn(store, "=", 1);
+ if(result)
+ goto error;
/* Generate value. */
- str = ASN1tostr(&value, 0);
- if(!str)
- return -1;
- for(p3 = str; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
- free((char *) str);
+ result = ASN1tostr(store, &value, 0);
+ if(result)
+ goto error;
+ Curl_dyn_reset(&temp);
+ added = TRUE; /* use separator for next */
}
}
+error:
+ Curl_dyn_free(&temp);
- return l;
+ return result;
}
#endif /* WANT_EXTRACT_CERTINFO */
@@ -876,25 +852,9 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
#ifdef WANT_EXTRACT_CERTINFO
-/*
- * Copy at most 64-characters, terminate with a newline and returns the
- * effective number of stored characters.
- */
-static size_t copySubstring(char *to, const char *from)
-{
- size_t i;
- for(i = 0; i < 64; i++) {
- to[i] = *from;
- if(!*from++)
- break;
- }
-
- to[i++] = '\n';
- return i;
-}
-
-static const char *dumpAlgo(struct Curl_asn1Element *param,
- const char *beg, const char *end)
+static CURLcode dumpAlgo(struct dynbuf *store,
+ struct Curl_asn1Element *param,
+ const char *beg, const char *end)
{
struct Curl_asn1Element oid;
@@ -902,14 +862,16 @@ static const char *dumpAlgo(struct Curl_asn1Element *param,
beg = getASN1Element(&oid, beg, end);
if(!beg)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
param->header = NULL;
param->tag = 0;
param->beg = param->end = end;
- if(beg < end)
- if(!getASN1Element(param, beg, end))
- return NULL;
- return OID2str(oid.beg, oid.end, TRUE);
+ if(beg < end) {
+ const char *p = getASN1Element(param, beg, end);
+ if(!p)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ return OID2str(store, oid.beg, oid.end, TRUE);
}
/*
@@ -926,24 +888,47 @@ static CURLcode ssl_push_certinfo(struct Curl_easy *data,
return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
}
-/* return 0 on success, 1 on error */
-static int do_pubkey_field(struct Curl_easy *data, int certnum,
- const char *label, struct Curl_asn1Element *elem)
+/*
+ * This is a convenience function for push_certinfo_len that takes a
+ * dynbuf value.
+ *
+ * It also does the verbose output if !certnum.
+ */
+static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ struct dynbuf *ptr)
{
- const char *output;
- CURLcode result = CURLE_OK;
+ size_t valuelen = Curl_dyn_len(ptr);
+ char *value = Curl_dyn_ptr(ptr);
+
+ CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
+ value, valuelen);
+
+ if(!certnum && !result)
+ infof(data, " %s: %s", label, value);
+
+ return result;
+}
+
+static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
+ const char *label,
+ struct Curl_asn1Element *elem)
+{
+ CURLcode result;
+ struct dynbuf out;
+
+ Curl_dyn_init(&out, MAX_X509_STR);
/* Generate a certificate information record for the public key. */
- output = ASN1tostr(elem, 0);
- if(output) {
+ result = ASN1tostr(&out, elem, 0);
+ if(!result) {
if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, label, output);
- if(!certnum && !result)
- infof(data, " %s: %s", label, output);
- free((char *) output);
+ result = ssl_push_certinfo_dyn(data, certnum, label, &out);
+ Curl_dyn_free(&out);
}
- return result ? 1 : 0;
+ return result;
}
/* return 0 on success, 1 on error */
@@ -964,7 +949,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
*/
const size_t len = ((pubkey->end - pubkey->beg - 2) * 4);
if(!certnum)
- infof(data, " ECC Public Key (%lu bits)", len);
+ infof(data, " ECC Public Key (%zu bits)", len);
if(data->set.ssl.certinfo) {
char q[sizeof(len) * 8 / 3 + 1];
(void)msnprintf(q, sizeof(q), "%zu", len);
@@ -998,7 +983,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
if(len > 32)
elem.beg = q; /* Strip leading zero bytes. */
if(!certnum)
- infof(data, " RSA Public Key (%lu bits)", len);
+ infof(data, " RSA Public Key (%zu bits)", len);
if(data->set.ssl.certinfo) {
char r[sizeof(len) * 8 / 3 + 1];
msnprintf(r, sizeof(r), "%zu", len);
@@ -1049,24 +1034,12 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
/*
* Convert an ASN.1 distinguished name into a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Return error.
*/
-static const char *DNtostr(struct Curl_asn1Element *dn)
+static CURLcode DNtostr(struct dynbuf *store,
+ struct Curl_asn1Element *dn)
{
- char *buf = NULL;
- ssize_t buflen = encodeDN(NULL, 0, dn);
-
- if(buflen >= 0) {
- buf = malloc(buflen + 1);
- if(buf) {
- if(encodeDN(buf, buflen + 1, dn) == -1) {
- free(buf);
- return NULL;
- }
- buf[buflen] = '\0';
- }
- }
- return buf;
+ return encodeDN(store, dn);
}
CURLcode Curl_extract_certinfo(struct Curl_easy *data,
@@ -1076,19 +1049,19 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
{
struct Curl_X509certificate cert;
struct Curl_asn1Element param;
- const char *ccp;
- char *cp1;
- size_t cl1;
- char *cp2;
+ char *certptr;
+ size_t clen;
+ struct dynbuf out;
CURLcode result = CURLE_OK;
unsigned int version;
- size_t i;
- size_t j;
+ const char *ptr;
+ int rc;
if(!data->set.ssl.certinfo)
if(certnum)
return CURLE_OK;
+ Curl_dyn_init(&out, MAX_X509_STR);
/* Prepare the certificate information for curl_easy_getinfo(). */
/* Extract the certificate ASN.1 elements. */
@@ -1096,135 +1069,126 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
return CURLE_PEER_FAILED_VERIFICATION;
/* Subject. */
- ccp = DNtostr(&cert.subject);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
+ result = DNtostr(&out, &cert.subject);
+ if(result)
+ goto done;
if(data->set.ssl.certinfo) {
- result = ssl_push_certinfo(data, certnum, "Subject", ccp);
+ result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out);
if(result)
- return result;
+ goto done;
}
- if(!certnum)
- infof(data, "%2d Subject: %s", certnum, ccp);
- free((char *) ccp);
+ Curl_dyn_reset(&out);
/* Issuer. */
- ccp = DNtostr(&cert.issuer);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
+ result = DNtostr(&out, &cert.issuer);
+ if(result)
+ goto done;
if(data->set.ssl.certinfo) {
- result = ssl_push_certinfo(data, certnum, "Issuer", ccp);
+ result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out);
+ if(result)
+ goto done;
}
- if(!certnum)
- infof(data, " Issuer: %s", ccp);
- free((char *) ccp);
- if(result)
- return result;
+ Curl_dyn_reset(&out);
/* Version (always fits in less than 32 bits). */
version = 0;
- for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
- version = (version << 8) | *(const unsigned char *) ccp;
+ for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
+ version = (version << 8) | *(const unsigned char *) ptr;
if(data->set.ssl.certinfo) {
- ccp = curl_maprintf("%x", version);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- result = ssl_push_certinfo(data, certnum, "Version", ccp);
- free((char *) ccp);
+ result = Curl_dyn_addf(&out, "%x", version);
+ if(result)
+ goto done;
+ result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
if(result)
- return result;
+ goto done;
+ Curl_dyn_reset(&out);
}
- if(!certnum)
- infof(data, " Version: %u (0x%x)", version + 1, version);
/* Serial number. */
- ccp = ASN1tostr(&cert.serialNumber, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Serial Number", ccp);
- if(!certnum)
- infof(data, " Serial Number: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.serialNumber, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Signature algorithm .*/
- ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
- cert.signatureAlgorithm.end);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
- if(!certnum)
- infof(data, " Signature Algorithm: %s", ccp);
- free((char *) ccp);
+ result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
+ cert.signatureAlgorithm.end);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm",
+ &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Start Date. */
- ccp = ASN1tostr(&cert.notBefore, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Start Date", ccp);
- if(!certnum)
- infof(data, " Start Date: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.notBefore, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Expire Date. */
- ccp = ASN1tostr(&cert.notAfter, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Expire Date", ccp);
- if(!certnum)
- infof(data, " Expire Date: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.notAfter, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Public Key Algorithm. */
- ccp = dumpAlgo(&param, cert.subjectPublicKeyAlgorithm.beg,
- cert.subjectPublicKeyAlgorithm.end);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Public Key Algorithm",
- ccp);
- if(!result) {
- int ret;
- if(!certnum)
- infof(data, " Public Key Algorithm: %s", ccp);
- ret = do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
- if(ret)
- result = CURLE_OUT_OF_MEMORY; /* the most likely error */
- }
- free((char *) ccp);
+ result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
+ cert.subjectPublicKeyAlgorithm.end);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm",
+ &out);
+ if(result)
+ goto done;
+ }
+
+ rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+ &param, &cert.subjectPublicKey);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY; /* the most likely error */
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Signature. */
- ccp = ASN1tostr(&cert.signature, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Signature", ccp);
- if(!certnum)
- infof(data, " Signature: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.signature, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Generate PEM certificate. */
result = Curl_base64_encode(cert.certificate.beg,
cert.certificate.end - cert.certificate.beg,
- &cp1, &cl1);
+ &certptr, &clen);
if(result)
- return result;
- /* Compute the number of characters in final certificate string. Format is:
+ goto done;
+
+ /* Generate the final output certificate string. Format is:
-----BEGIN CERTIFICATE-----\n
<max 64 base64 characters>\n
.
@@ -1232,207 +1196,34 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
.
-----END CERTIFICATE-----\n
*/
- i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26;
- cp2 = malloc(i + 1);
- if(!cp2) {
- free(cp1);
- return CURLE_OUT_OF_MEMORY;
- }
- /* Build the certificate string. */
- i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----");
- for(j = 0; j < cl1; j += 64)
- i += copySubstring(cp2 + i, cp1 + j);
- i += copySubstring(cp2 + i, "-----END CERTIFICATE-----");
- cp2[i] = '\0';
- free(cp1);
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Cert", cp2);
- if(!certnum)
- infof(data, "%s", cp2);
- free(cp2);
- return result;
-}
-#endif /* WANT_EXTRACT_CERTINFO */
-
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+ Curl_dyn_reset(&out);
-#ifdef WANT_VERIFYHOST
-
-static const char *checkOID(const char *beg, const char *end,
- const char *oid)
-{
- struct Curl_asn1Element e;
- const char *ccp;
- const char *p;
- bool matched;
-
- /* Check if first ASN.1 element at `beg' is the given OID.
- Return a pointer in the source after the OID if found, else NULL. */
-
- ccp = getASN1Element(&e, beg, end);
- if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER)
- return NULL;
-
- p = OID2str(e.beg, e.end, FALSE);
- if(!p)
- return NULL;
-
- matched = !strcmp(p, oid);
- free((char *) p);
- return matched? ccp: NULL;
-}
-
-CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char *beg, const char *end)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct Curl_X509certificate cert;
- struct Curl_asn1Element dn;
- struct Curl_asn1Element elem;
- struct Curl_asn1Element ext;
- struct Curl_asn1Element name;
- const char *p;
- const char *q;
- char *dnsname;
- int matched = -1;
- size_t addrlen = (size_t) -1;
- ssize_t len;
- size_t hostlen;
-
-#ifdef ENABLE_IPV6
- struct in6_addr addr;
-#else
- struct in_addr addr;
-#endif
-
- /* Verify that connection server matches info in X509 certificate at
- `beg'..`end'. */
-
- if(!conn_config->verifyhost)
- return CURLE_OK;
-
- if(Curl_parseX509(&cert, beg, end))
- return CURLE_PEER_FAILED_VERIFICATION;
-
- hostlen = strlen(connssl->peer.hostname);
-
- /* Get the server IP address. */
-#ifdef ENABLE_IPV6
- if(cf->conn->bits.ipv6_ip &&
- Curl_inet_pton(AF_INET6, connssl->peer.hostname, &addr))
- addrlen = sizeof(struct in6_addr);
- else
-#endif
- if(Curl_inet_pton(AF_INET, connssl->peer.hostname, &addr))
- addrlen = sizeof(struct in_addr);
-
- /* Process extensions. */
- for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) {
- p = getASN1Element(&ext, p, cert.extensions.end);
- if(!p)
- return CURLE_PEER_FAILED_VERIFICATION;
-
- /* Check if extension is a subjectAlternativeName. */
- ext.beg = checkOID(ext.beg, ext.end, sanOID);
- if(ext.beg) {
- ext.beg = getASN1Element(&elem, ext.beg, ext.end);
- if(!ext.beg)
- return CURLE_PEER_FAILED_VERIFICATION;
- /* Skip critical if present. */
- if(elem.tag == CURL_ASN1_BOOLEAN) {
- ext.beg = getASN1Element(&elem, ext.beg, ext.end);
- if(!ext.beg)
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- /* Parse the octet string contents: is a single sequence. */
- if(!getASN1Element(&elem, elem.beg, elem.end))
- return CURLE_PEER_FAILED_VERIFICATION;
- /* Check all GeneralNames. */
- for(q = elem.beg; matched != 1 && q < elem.end;) {
- q = getASN1Element(&name, q, elem.end);
- if(!q)
- break;
- switch(name.tag) {
- case 2: /* DNS name. */
- len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
- name.beg, name.end);
- if(len > 0 && (size_t)len == strlen(dnsname))
- matched = Curl_cert_hostcheck(dnsname, (size_t)len,
- connssl->peer.hostname, hostlen);
- else
- matched = 0;
- free(dnsname);
- break;
-
- case 7: /* IP address. */
- matched = (size_t)(name.end - name.beg) == addrlen &&
- !memcmp(&addr, name.beg, addrlen);
- break;
- }
- }
- }
- }
-
- switch(matched) {
- case 1:
- /* an alternative name matched the server hostname */
- infof(data, " subjectAltName: %s matched", connssl->dispname);
- return CURLE_OK;
- case 0:
- /* an alternative name field existed, but didn't match and then
- we MUST fail */
- infof(data, " subjectAltName does not match %s", connssl->dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
-
- /* Process subject. */
- name.header = NULL;
- name.beg = name.end = "";
- q = cert.subject.beg;
- /* we have to look to the last occurrence of a commonName in the
- distinguished one to get the most significant one. */
- while(q < cert.subject.end) {
- q = getASN1Element(&dn, q, cert.subject.end);
- if(!q)
- break;
- for(p = dn.beg; p < dn.end;) {
- p = getASN1Element(&elem, p, dn.end);
- if(!p)
- return CURLE_PEER_FAILED_VERIFICATION;
- /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */
- elem.beg = checkOID(elem.beg, elem.end, cnOID);
- if(elem.beg)
- name = elem; /* Latch CN. */
- }
- }
-
- /* Check the CN if found. */
- if(!getASN1Element(&elem, name.beg, name.end))
- failf(data, "SSL: unable to obtain common name from peer certificate");
- else {
- len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
- if(len < 0) {
- free(dnsname);
- return CURLE_OUT_OF_MEMORY;
- }
- if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */
- failf(data, "SSL: illegal cert name field");
- else if(Curl_cert_hostcheck((const char *) dnsname,
- len, connssl->peer.hostname, hostlen)) {
- infof(data, " common name: %s (matched)", dnsname);
- free(dnsname);
- return CURLE_OK;
+ /* Build the certificate string. */
+ result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+ if(!result) {
+ size_t j = 0;
+
+ while(!result && (j < clen)) {
+ size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
+ result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+ if(!result)
+ result = Curl_dyn_addn(&out, "\n", 1);
+ j += chunksize;
}
- else
- failf(data, "SSL: certificate subject name '%s' does not match "
- "target host name '%s'", dnsname, connssl->dispname);
- free(dnsname);
+ if(!result)
+ result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
}
+ free(certptr);
+ if(!result)
+ if(data->set.ssl.certinfo)
+ result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out);
- return CURLE_PEER_FAILED_VERIFICATION;
+done:
+ Curl_dyn_free(&out);
+ return result;
}
-#endif /* WANT_VERIFYHOST */
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
diff --git a/libs/libcurl/src/ws.c b/libs/libcurl/src/ws.c
index 5c8db777f1..0caca18f05 100644
--- a/libs/libcurl/src/ws.c
+++ b/libs/libcurl/src/ws.c
@@ -24,7 +24,7 @@
#include "curl_setup.h"
#include <curl/curl.h>
-#ifdef USE_WEBSOCKETS
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#include "urldata.h"
#include "bufq.h"
@@ -225,6 +225,10 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
dec->payload_len = (dec->head[2] << 8) | dec->head[3];
break;
case 10:
+ if(dec->head[2] > 127) {
+ failf(data, "WS: frame length longer than 64 signed not supported");
+ return CURLE_RECV_ERROR;
+ }
dec->payload_len = ((curl_off_t)dec->head[2] << 56) |
(curl_off_t)dec->head[3] << 48 |
(curl_off_t)dec->head[4] << 40 |
@@ -296,7 +300,7 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
case WS_DEC_INIT:
ws_dec_reset(dec);
dec->state = WS_DEC_HEAD;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case WS_DEC_HEAD:
result = ws_dec_read_head(dec, data, inraw);
if(result) {
@@ -321,7 +325,7 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
dec->state = WS_DEC_INIT;
break;
}
- /* FALLTHROUGH */
+ FALLTHROUGH();
case WS_DEC_PAYLOAD:
result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx);
ws_dec_info(dec, data, "passing");
@@ -350,6 +354,136 @@ static void update_meta(struct websocket *ws,
ws->frame.bytesleft = (payload_len - payload_offset - cur_len);
}
+/* WebSockets decoding client writer */
+struct ws_cw_ctx {
+ struct Curl_cwriter super;
+ struct bufq buf;
+};
+
+static CURLcode ws_cw_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
+{
+ struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+ (void)data;
+ Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT);
+ return CURLE_OK;
+}
+
+static void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer)
+{
+ struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+ (void) data;
+ Curl_bufq_free(&ctx->buf);
+}
+
+struct ws_cw_dec_ctx {
+ struct Curl_easy *data;
+ struct websocket *ws;
+ struct Curl_cwriter *next_writer;
+ int cw_type;
+};
+
+static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen,
+ int frame_age, int frame_flags,
+ curl_off_t payload_offset,
+ curl_off_t payload_len,
+ void *user_data,
+ CURLcode *err)
+{
+ struct ws_cw_dec_ctx *ctx = user_data;
+ struct Curl_easy *data = ctx->data;
+ struct websocket *ws = ctx->ws;
+ curl_off_t remain = (payload_len - (payload_offset + buflen));
+
+ (void)frame_age;
+ if((frame_flags & CURLWS_PING) && !remain) {
+ /* auto-respond to PINGs, only works for single-frame payloads atm */
+ size_t bytes;
+ infof(data, "WS: auto-respond to PING with a PONG");
+ /* send back the exact same content as a PONG */
+ *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
+ if(*err)
+ return -1;
+ }
+ else if(buflen || !remain) {
+ /* forward the decoded frame to the next client writer. */
+ update_meta(ws, frame_age, frame_flags, payload_offset,
+ payload_len, buflen);
+
+ *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type,
+ (const char *)buf, buflen);
+ if(*err)
+ return -1;
+ }
+ *err = CURLE_OK;
+ return (ssize_t)buflen;
+}
+
+static CURLcode ws_cw_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
+{
+ struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+ struct websocket *ws;
+ CURLcode result;
+
+ if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
+ ws = data->conn->proto.ws;
+ if(!ws) {
+ failf(data, "WS: not a websocket transfer");
+ return CURLE_FAILED_INIT;
+ }
+
+ if(nbytes) {
+ ssize_t nwritten;
+ nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf,
+ nbytes, &result);
+ if(nwritten < 0) {
+ infof(data, "WS: error adding data to buffer %d", result);
+ return result;
+ }
+ }
+
+ while(!Curl_bufq_is_empty(&ctx->buf)) {
+ struct ws_cw_dec_ctx pass_ctx;
+ pass_ctx.data = data;
+ pass_ctx.ws = ws;
+ pass_ctx.next_writer = writer->next;
+ pass_ctx.cw_type = type;
+ result = ws_dec_pass(&ws->dec, data, &ctx->buf,
+ ws_cw_dec_next, &pass_ctx);
+ if(result == CURLE_AGAIN)
+ /* insufficient amount of data, keep it for later.
+ * we pretend to have written all since we have a copy */
+ return CURLE_OK;
+ else if(result) {
+ infof(data, "WS: decode error %d", (int)result);
+ return result;
+ }
+ }
+
+ if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
+ infof(data, "WS: decode ending with %zd frame bytes remaining",
+ Curl_bufq_len(&ctx->buf));
+ return CURLE_RECV_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+/* WebSocket payload decoding client writer. */
+static const struct Curl_cwtype ws_cw_decode = {
+ "ws-decode",
+ NULL,
+ ws_cw_init,
+ ws_cw_write,
+ ws_cw_close,
+ sizeof(struct ws_cw_ctx)
+};
+
+
static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
const char *msg)
{
@@ -410,6 +544,13 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
size_t hlen;
ssize_t n;
+ if(payload_len < 0) {
+ failf(data, "WS: starting new frame with negative payload length %"
+ CURL_FORMAT_CURL_OFF_T, payload_len);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
if(enc->payload_remain > 0) {
/* trying to write a new frame before the previous one is finished */
failf(data, "WS: starting new frame with %zd bytes from last one"
@@ -607,6 +748,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
{
struct SingleRequest *k = &data->req;
struct websocket *ws;
+ struct Curl_cwriter *ws_dec_writer;
CURLcode result;
DEBUGASSERT(data->conn);
@@ -616,7 +758,8 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
if(!ws)
return CURLE_OUT_OF_MEMORY;
data->conn->proto.ws = ws;
- Curl_bufq_init(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT);
+ Curl_bufq_init2(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
+ BUFQ_OPT_SOFT_LIMIT);
Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
BUFQ_OPT_SOFT_LIMIT);
ws_dec_init(&ws->dec);
@@ -655,6 +798,18 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
+ /* Install our client writer that decodes WS frames payload */
+ result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode,
+ CURL_CW_CONTENT_DECODE);
+ if(result)
+ return result;
+
+ result = Curl_cwriter_add(data, ws_dec_writer);
+ if(result) {
+ Curl_cwriter_free(data, ws_dec_writer);
+ return result;
+ }
+
if(data->set.connect_only) {
ssize_t nwritten;
/* In CONNECT_ONLY setup, the payloads from `mem` need to be received
@@ -666,105 +821,15 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
return result;
infof(data, "%zu bytes websocket payload", nread);
}
- k->upgr101 = UPGR101_RECEIVED;
-
- return result;
-}
-
-static ssize_t ws_client_write(const unsigned char *buf, size_t buflen,
- int frame_age, int frame_flags,
- curl_off_t payload_offset,
- curl_off_t payload_len,
- void *userp,
- CURLcode *err)
-{
- struct Curl_easy *data = userp;
- struct websocket *ws;
- size_t wrote;
- curl_off_t remain = (payload_len - (payload_offset + buflen));
-
- (void)frame_age;
- if(!data->conn || !data->conn->proto.ws) {
- *err = CURLE_FAILED_INIT;
- return -1;
- }
- ws = data->conn->proto.ws;
-
- if((frame_flags & CURLWS_PING) && !remain) {
- /* auto-respond to PINGs, only works for single-frame payloads atm */
- size_t bytes;
- infof(data, "WS: auto-respond to PING with a PONG");
- /* send back the exact same content as a PONG */
- *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
- if(*err)
- return -1;
- }
- else if(buflen || !remain) {
- /* deliver the decoded frame to the user callback. The application
- * may invoke curl_ws_meta() to access frame information. */
- update_meta(ws, frame_age, frame_flags, payload_offset,
- payload_len, buflen);
- Curl_set_in_callback(data, true);
- wrote = data->set.fwrite_func((char *)buf, 1,
- buflen, data->set.out);
- Curl_set_in_callback(data, false);
- if(wrote != buflen) {
- *err = CURLE_RECV_ERROR;
- return -1;
+ else { /* !connect_only */
+ /* And pass any additional data to the writers */
+ if(nread) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread);
}
}
- *err = CURLE_OK;
- return (ssize_t)buflen;
-}
-
-/* Curl_ws_writecb() is the write callback for websocket traffic. The
- websocket data is provided to this raw, in chunks. This function should
- handle/decode the data and call the "real" underlying callback accordingly.
-*/
-size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
- size_t nitems, void *userp)
-{
- struct Curl_easy *data = userp;
-
- if(data->set.ws_raw_mode)
- return data->set.fwrite_func(buffer, size, nitems, data->set.out);
- else if(nitems) {
- struct websocket *ws;
- CURLcode result;
-
- if(!data->conn || !data->conn->proto.ws) {
- failf(data, "WS: not a websocket transfer");
- return nitems - 1;
- }
- ws = data->conn->proto.ws;
-
- if(buffer) {
- ssize_t nwritten;
-
- nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)buffer,
- nitems, &result);
- if(nwritten < 0) {
- infof(data, "WS: error adding data to buffer %d", (int)result);
- return nitems - 1;
- }
- buffer = NULL;
- }
-
- while(!Curl_bufq_is_empty(&ws->recvbuf)) {
+ k->upgr101 = UPGR101_RECEIVED;
- result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
- ws_client_write, data);
- if(result == CURLE_AGAIN)
- /* insufficient amount of data, keep it for later.
- * we pretend to have written all since we have a copy */
- return nitems;
- else if(result) {
- infof(data, "WS: decode error %d", (int)result);
- return nitems - 1;
- }
- }
- }
- return nitems;
+ return result;
}
struct ws_collect {
@@ -997,8 +1062,11 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
ws = data->conn->proto.ws;
if(data->set.ws_raw_mode) {
- if(fragsize || flags)
+ if(fragsize || flags) {
+ DEBUGF(infof(data, "ws_send: "
+ "fragsize and flags cannot be non-zero in raw mode"));
return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
if(!buflen)
/* nothing to do */
return CURLE_OK;
@@ -1071,14 +1139,23 @@ static void ws_free(struct connectdata *conn)
}
}
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* websockets is 1.1 only (for now) */
+ data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ return Curl_http_setup_conn(data, conn);
+}
+
+
void Curl_ws_done(struct Curl_easy *data)
{
(void)data;
}
-CURLcode Curl_ws_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
+static CURLcode ws_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
{
(void)data;
(void)dead_connection;
@@ -1096,6 +1173,57 @@ CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
return NULL;
}
+const struct Curl_handler Curl_handler_ws = {
+ "WS", /* scheme */
+ ws_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ws_disconnect, /* disconnect */
+ Curl_http_write_resp, /* write_resp */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_WS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_CREDSPERREQUEST | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+
+#ifdef USE_SSL
+const struct Curl_handler Curl_handler_wss = {
+ "WSS", /* scheme */
+ ws_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ NULL, /* proto_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ws_disconnect, /* disconnect */
+ Curl_http_write_resp, /* write_resp */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTPS, /* defport */
+ CURLPROTO_WSS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+#endif
+
+
#else
CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
diff --git a/libs/libcurl/src/ws.h b/libs/libcurl/src/ws.h
index 20b9103ac6..af26e3f140 100644
--- a/libs/libcurl/src/ws.h
+++ b/libs/libcurl/src/ws.h
@@ -25,7 +25,7 @@
***************************************************************************/
#include "curl_setup.h"
-#ifdef USE_WEBSOCKETS
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#ifdef USE_HYPER
#define REQTYPE void
@@ -75,11 +75,14 @@ struct websocket {
CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
-size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
void Curl_ws_done(struct Curl_easy *data);
-CURLcode Curl_ws_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection);
+
+extern const struct Curl_handler Curl_handler_ws;
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_wss;
+#endif
+
+
#else
#define Curl_ws_request(x,y) CURLE_OK
#define Curl_ws_done(x) Curl_nop_stmt