diff --git a/deps/curl/curl.gyp b/deps/curl/curl.gyp index a8049d0d82..5cc39bcb88 100644 --- a/deps/curl/curl.gyp +++ b/deps/curl/curl.gyp @@ -14,12 +14,14 @@ 'lib/cf-dns.c', 'lib/cf-https-connect.c', 'lib/cf-ip-happy.c', + 'lib/cf-setup.c', 'lib/cf-socket.c', 'lib/cfilters.c', 'lib/conncache.c', 'lib/connect.c', 'lib/content_encoding.c', 'lib/cookie.c', + 'lib/creds.c', 'lib/cshutdn.c', 'lib/curl_addrinfo.c', 'lib/curl_endian.c', @@ -90,6 +92,7 @@ 'lib/netrc.c', 'lib/openldap.c', 'lib/parsedate.c', + 'lib/peer.c', 'lib/pingpong.c', 'lib/pop3.c', 'lib/progress.c', @@ -172,6 +175,7 @@ 'lib/vtls/schannel.c', 'lib/vtls/schannel_verify.c', 'lib/vtls/vtls.c', + 'lib/vtls/vtls_config.c', 'lib/vtls/vtls_scache.c', 'lib/vtls/vtls_spack.c', 'lib/vtls/x509asn1.c' diff --git a/deps/curl/include/Makefile.in b/deps/curl/include/Makefile.in index 7c84707850..66e7e27cb5 100644 --- a/deps/curl/include/Makefile.in +++ b/deps/curl/include/Makefile.in @@ -252,6 +252,7 @@ FILECMD = @FILECMD@ FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ GCOV = @GCOV@ GREP = @GREP@ +H2O = @H2O@ HAVE_LIBZ = @HAVE_LIBZ@ HTTPD = @HTTPD@ HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ diff --git a/deps/curl/include/curl/Makefile.in b/deps/curl/include/curl/Makefile.in index 727de84f36..cf1aa85e98 100644 --- a/deps/curl/include/curl/Makefile.in +++ b/deps/curl/include/curl/Makefile.in @@ -239,6 +239,7 @@ FILECMD = @FILECMD@ FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ GCOV = @GCOV@ GREP = @GREP@ +H2O = @H2O@ HAVE_LIBZ = @HAVE_LIBZ@ HTTPD = @HTTPD@ HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ diff --git a/deps/curl/include/curl/curl.h b/deps/curl/include/curl/curl.h index 6961a6c4c1..7e59e43c4d 100644 --- a/deps/curl/include/curl/curl.h +++ b/deps/curl/include/curl/curl.h @@ -252,7 +252,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE (10*1024*1024) +#define CURL_MAX_READ_SIZE (10 * 1024 * 1024) #endif #ifndef CURL_MAX_WRITE_SIZE @@ -269,7 +269,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, /* The only reason to have a max limit for this is to avoid the risk of a bad server feeding libcurl with a never-ending header that causes reallocs infinitely */ -#define CURL_MAX_HTTP_HEADER (100*1024) +#define CURL_MAX_HTTP_HEADER (100 * 1024) #endif /* This is a magic return code for the write callback that, when returned, @@ -593,7 +593,7 @@ typedef enum { CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind that failed */ - CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialize ENGINE */ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not accepted and we failed to login */ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ @@ -802,9 +802,11 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ #define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along the hostname rather than the IP address. added in 7.18.0 */ +#define CURLPROXY_HTTPS3 8L /* HTTPS and attempt HTTP/3 + added in 8.21.0 */ typedef enum { - CURLPROXY_LAST = 8 /* never use */ + CURLPROXY_LAST = 9 /* never use */ } curl_proxytype; /* this enum was added in 7.10 */ /* @@ -816,7 +818,7 @@ typedef enum { * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) * CURLAUTH_NTLM - HTTP NTLM authentication - * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavor * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper * CURLAUTH_BEARER - HTTP Bearer token authentication * CURLAUTH_ONLY - Use together with a single other type to force no @@ -902,16 +904,16 @@ enum curl_khmatch { }; typedef int - (*curl_sshkeycallback) (CURL *easy, /* easy handle */ - const struct curl_khkey *knownkey, /* known */ - const struct curl_khkey *foundkey, /* found */ - enum curl_khmatch, /* libcurl's view on the keys */ - void *clientp); /* custom pointer passed with */ - /* CURLOPT_SSH_KEYDATA */ + (*curl_sshkeycallback)(CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed with */ + /* CURLOPT_SSH_KEYDATA */ typedef int - (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ - /* with CURLOPT_SSH_HOSTKEYDATA */ + (*curl_sshhostkeycallback)(void *clientp,/* custom pointer passed */ + /* with CURLOPT_SSH_HOSTKEYDATA */ int keytype, /* CURLKHTYPE */ const char *key, /* hostkey to check */ size_t keylen); /* length of the key */ @@ -932,7 +934,7 @@ typedef enum { /* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the name of improving interoperability with older servers. Some SSL libraries - have introduced work-arounds for this flaw but those work-arounds sometimes + have introduced workarounds for this flaw but those workarounds sometimes make the SSL communication fail. To regain functionality with those broken servers, a user can this way allow the vulnerability back. */ #define CURLSSLOPT_ALLOW_BEAST (1L << 0) @@ -1362,7 +1364,7 @@ typedef enum { CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), - /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + /* Set if we should verify the peer in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate @@ -1420,7 +1422,7 @@ typedef enum { */ CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), - /* Set if we should verify the Common name from the peer certificate in ssl + /* Set if we should verify the Common name from the peer certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), @@ -1494,8 +1496,8 @@ typedef enum { CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and - CURLPROXY_SOCKS5. */ + CURLPROXY_HTTPS, CURLPROXY_HTTPS2, CURLPROXY_HTTPS3, CURLPROXY_SOCKS4, + CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like @@ -1524,12 +1526,12 @@ typedef enum { Note that setting multiple bits may cause extra network round-trips. */ CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), - /* Set the ssl context callback function, currently only for OpenSSL or + /* Set the SSL context callback function, currently only for OpenSSL or wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. The function must match the curl_ssl_ctx_callback prototype. */ CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), - /* Set the userdata for the ssl context callback function's third + /* Set the userdata for the SSL context callback function's third argument */ CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109), @@ -1935,11 +1937,11 @@ typedef enum { /* Set authentication options directly */ CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), - /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS NPN extension (http2 over SSL might fail without) */ CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, 7.86.0, "Has no function"), - /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS ALPN extension (http2 over SSL might fail without) */ CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), /* Time to wait for a response to an HTTP request containing an @@ -1985,10 +1987,12 @@ typedef enum { CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), /* Set stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240, + 8.21.0, "Has no function"), /* Set E-xclusive stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241, + 8.21.0, "Has no function"), /* Do not send any tftp option requests to the server */ CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), @@ -2012,11 +2016,11 @@ typedef enum { this option is used only if PROXY_SSL_VERIFYPEER is true */ CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), - /* Set if we should verify the proxy in ssl handshake, + /* Set if we should verify the proxy in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), - /* Set if we should verify the Common name from the proxy certificate in ssl + /* Set if we should verify the Common name from the proxy certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches * the provided hostname. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), @@ -2808,14 +2812,14 @@ struct curl_slist { * backend can also be specified via the name parameter (passing -1 as id). If * both id and name are specified, the name is ignored. If neither id nor * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND - * and set the "avail" pointer to the NULL-terminated list of available + * and set the "avail" pointer to the null-terminated list of available * backends. * * Upon success, the function returns CURLSSLSET_OK. * * If the specified SSL backend is not available, the function returns * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a - * NULL-terminated list of available SSL backends. + * null-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. diff --git a/deps/curl/include/curl/curlver.h b/deps/curl/include/curl/curlver.h index de09eaf8ca..683098e172 100644 --- a/deps/curl/include/curl/curlver.h +++ b/deps/curl/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.20.0" +#define LIBCURL_VERSION "8.21.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 20 +#define LIBCURL_VERSION_MINOR 21 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always @@ -47,7 +47,7 @@ Where XX, YY and ZZ are the main version, release and patch numbers in hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + using two digits. Version 1.2 would appear as "0x010200" while 9.11.7 appears as "0x090b07". This 6-digit (24 bits) hexadecimal number does not show pre-release number, @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081400 +#define LIBCURL_VERSION_NUM 0x081500 /* * This is the date and time when the full source package was created. The @@ -69,7 +69,7 @@ * * "2007-11-23" */ -#define LIBCURL_TIMESTAMP "2026-04-29" +#define LIBCURL_TIMESTAMP "2026-06-24" #define CURL_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z)) #define CURL_AT_LEAST_VERSION(x, y, z) \ diff --git a/deps/curl/include/curl/system.h b/deps/curl/include/curl/system.h index c2dbab56e0..30216ea34c 100644 --- a/deps/curl/include/curl/system.h +++ b/deps/curl/include/curl/system.h @@ -297,7 +297,7 @@ /* ===================================== */ #elif defined(_MSC_VER) -# if (_MSC_VER >= 1800) +# if _MSC_VER >= 1800 # include # define CURL_FORMAT_CURL_OFF_T PRId64 # define CURL_FORMAT_CURL_OFF_TU PRIu64 diff --git a/deps/curl/lib/CMakeLists.txt b/deps/curl/lib/CMakeLists.txt index aae466c677..3ef623536e 100644 --- a/deps/curl/lib/CMakeLists.txt +++ b/deps/curl/lib/CMakeLists.txt @@ -110,8 +110,7 @@ if(SHARE_LIB_OBJECT) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") endif() target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) - set_target_properties(${LIB_OBJECT} PROPERTIES - POSITION_INDEPENDENT_CODE ON) + set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") @@ -253,7 +252,7 @@ if(BUILD_SHARED_LIBS) CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR # OpenHarmony CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR - # FreeBSD comes with the a.out and ELF flavours but a.out was supported + # FreeBSD comes with the a.out and ELF flavors but a.out was supported # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake # on those ancient systems. CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") diff --git a/deps/curl/lib/Makefile.in b/deps/curl/lib/Makefile.in index ff464a9c6c..548c2c10f2 100644 --- a/deps/curl/lib/Makefile.in +++ b/deps/curl/lib/Makefile.in @@ -219,39 +219,42 @@ libcurl_la_LIBADD = am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-dns.c \ cf-h1-proxy.c cf-h2-proxy.c cf-haproxy.c cf-https-connect.c \ - cf-ip-happy.c cf-socket.c cfilters.c conncache.c connect.c \ - content_encoding.c cookie.c cshutdn.c curl_addrinfo.c \ - curl_endian.c curl_fnmatch.c curl_fopen.c curl_get_line.c \ - curl_gethostname.c curl_gssapi.c curl_memrchr.c \ - curl_ntlm_core.c curl_range.c curl_sasl.c curl_sha512_256.c \ - curl_share.c curl_sspi.c curl_threads.c curl_trc.c cw-out.c \ - cw-pause.c dict.c dnscache.c doh.c dynhds.c easy.c \ - easygetopt.c easyoptions.c escape.c fake_addrinfo.c file.c \ - fileinfo.c formdata.c ftp.c ftplistparser.c getenv.c getinfo.c \ - gopher.c hash.c headers.c hmac.c hostip.c hostip4.c hostip6.c \ - hsts.c http.c http1.c http2.c http_aws_sigv4.c http_chunks.c \ - http_digest.c http_negotiate.c http_ntlm.c http_proxy.c \ - httpsrr.c idn.c if2ip.c imap.c ldap.c llist.c macos.c md4.c \ - md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \ - multi_ntfy.c netrc.c noproxy.c openldap.c parsedate.c \ - pingpong.c pop3.c progress.c protocol.c psl.c rand.c \ - ratelimit.c request.c rtsp.c select.c sendf.c setopt.c \ - sha256.c slist.c smb.c smtp.c socketpair.c socks.c \ - socks_gssapi.c socks_sspi.c splay.c strcase.c strequal.c \ - strerror.c system_win32.c telnet.c tftp.c thrdpool.c \ - thrdqueue.c transfer.c uint-bset.c uint-hash.c uint-spbset.c \ - uint-table.c url.c urlapi.c version.c ws.c vauth/cleartext.c \ - vauth/cram.c vauth/digest.c vauth/digest_sspi.c vauth/gsasl.c \ - vauth/krb5_gssapi.c vauth/krb5_sspi.c vauth/ntlm.c \ - vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_gssapi.c \ - vauth/spnego_sspi.c vauth/vauth.c vtls/apple.c \ - vtls/cipher_suite.c vtls/gtls.c vtls/hostcheck.c vtls/keylog.c \ - vtls/mbedtls.c vtls/openssl.c vtls/rustls.c vtls/schannel.c \ - vtls/schannel_verify.c vtls/vtls.c vtls/vtls_scache.c \ + cf-ip-happy.c cf-recvbuf.c cf-setup.c cf-socket.c cfilters.c \ + conncache.c connect.c content_encoding.c cookie.c creds.c \ + cshutdn.c curl_addrinfo.c curl_endian.c curl_fnmatch.c \ + curl_fopen.c curl_get_line.c curl_gethostname.c curl_gssapi.c \ + curl_memrchr.c curl_ntlm_core.c curl_range.c curl_sasl.c \ + curl_sha512_256.c curl_share.c curl_sspi.c curl_threads.c \ + curl_trc.c cw-out.c cw-pause.c dict.c dnscache.c doh.c \ + dynhds.c easy.c easygetopt.c easyoptions.c escape.c \ + fake_addrinfo.c file.c fileinfo.c formdata.c ftp.c \ + ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \ + hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \ + http2.c http_aws_sigv4.c http_chunks.c http_digest.c \ + http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \ + if2ip.c imap.c ldap.c llist.c macos.c md4.c md5.c memdebug.c \ + mime.c mprintf.c mqtt.c multi.c multi_ev.c multi_ntfy.c \ + netrc.c openldap.c parsedate.c peer.c pingpong.c pop3.c \ + progress.c protocol.c proxy.c psl.c rand.c ratelimit.c \ + request.c rtsp.c select.c sendf.c setopt.c sha256.c slist.c \ + smb.c smtp.c socketpair.c socks.c socks_gssapi.c socks_sspi.c \ + splay.c strcase.c strequal.c strerror.c system_win32.c \ + telnet.c tftp.c thrdpool.c thrdqueue.c transfer.c uint-bset.c \ + uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \ + version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \ + vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \ + vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \ + vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \ + vauth/vauth.c vtls/apple.c vtls/cipher_suite.c vtls/gtls.c \ + vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c vtls/openssl.c \ + vtls/rustls.c vtls/schannel.c vtls/schannel_verify.c \ + vtls/vtls.c vtls/vtls_config.c vtls/vtls_scache.c \ vtls/vtls_spack.c vtls/wolfssl.c vtls/x509asn1.c \ - vquic/curl_ngtcp2.c vquic/curl_quiche.c vquic/vquic.c \ - vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c vssh/vssh.c \ - curlx/base64.c curlx/basename.c curlx/dynbuf.c curlx/fopen.c \ + vquic/capsule.c vquic/cf-capsule.c vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c vquic/vquic.c vquic/vquic-tls.c \ + vssh/libssh.c vssh/libssh2.c vssh/vssh.c curlx/base64.c \ + curlx/basename.c curlx/dynbuf.c curlx/fopen.c \ curlx/inet_ntop.c curlx/inet_pton.c curlx/multibyte.c \ curlx/nonblock.c curlx/snprintf.c curlx/strcopy.c \ curlx/strdup.c curlx/strerr.c curlx/strparse.c \ @@ -259,11 +262,12 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ curlx/wait.c curlx/warnless.c curlx/winapi.c dllmain.c \ altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h bufref.h \ cf-dns.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \ - cf-https-connect.h cf-ip-happy.h cf-socket.h cfilters.h \ - conncache.h cshutdn.h connect.h content_encoding.h cookie.h \ - curl_addrinfo.h curl_ctype.h curl_endian.h curl_fnmatch.h \ - curl_fopen.h curl_get_line.h curl_gethostname.h curl_gssapi.h \ - curl_hmac.h curl_ldap.h curl_md4.h curl_md5.h curl_memrchr.h \ + cf-https-connect.h cf-ip-happy.h cf-recvbuf.h cf-setup.h \ + cf-socket.h cfilters.h conncache.h cshutdn.h connect.h \ + content_encoding.h cookie.h creds.h curl_addrinfo.h \ + curl_ctype.h curl_endian.h curl_fnmatch.h curl_fopen.h \ + curl_get_line.h curl_gethostname.h curl_gssapi.h curl_hmac.h \ + curl_ldap.h curl_md4.h curl_md5.h curl_memrchr.h \ curl_ntlm_core.h curl_printf.h curl_range.h curl_sasl.h \ curl_setup.h curl_sha256.h curl_sha512_256.h curl_share.h \ curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \ @@ -274,9 +278,9 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ http1.h http2.h http_aws_sigv4.h http_chunks.h http_digest.h \ http_negotiate.h http_ntlm.h http_proxy.h httpsrr.h idn.h \ if2ip.h imap.h llist.h macos.h mime.h mqtt.h multihandle.h \ - multi_ev.h multi_ntfy.h multiif.h netrc.h noproxy.h \ - parsedate.h pingpong.h pop3.h progress.h protocol.h psl.h \ - rand.h ratelimit.h request.h rtsp.h select.h sendf.h setopt.h \ + multi_ev.h multi_ntfy.h multiif.h netrc.h parsedate.h peer.h \ + pingpong.h pop3.h progress.h protocol.h proxy.h psl.h rand.h \ + ratelimit.h request.h rtsp.h select.h sendf.h setopt.h \ setup-os400.h setup-vms.h setup-win32.h sigpipe.h slist.h \ smb.h smtp.h sockaddr.h socketpair.h socks.h splay.h strcase.h \ strerror.h system_win32.h telnet.h tftp.h thrdpool.h \ @@ -285,9 +289,11 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ vauth/vauth.h vtls/apple.h vtls/cipher_suite.h vtls/gtls.h \ vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h vtls/openssl.h \ vtls/rustls.h vtls/schannel.h vtls/schannel_int.h vtls/vtls.h \ - vtls/vtls_int.h vtls/vtls_scache.h vtls/vtls_spack.h \ - vtls/wolfssl.h vtls/x509asn1.h vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \ + vtls/vtls_config.h vtls/vtls_int.h vtls/vtls_scache.h \ + vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \ + vquic/capsule.h vquic/cf-capsule.h vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h vquic/vquic.h vquic/vquic_int.h \ vquic/vquic-tls.h vssh/vssh.h vssh/ssh.h curlx/base64.h \ curlx/basename.h curlx/dynbuf.h curlx/fopen.h \ curlx/inet_ntop.h curlx/inet_pton.h curlx/multibyte.h \ @@ -301,10 +307,11 @@ am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \ libcurl_la-bufref.lo libcurl_la-cf-dns.lo \ libcurl_la-cf-h1-proxy.lo libcurl_la-cf-h2-proxy.lo \ libcurl_la-cf-haproxy.lo libcurl_la-cf-https-connect.lo \ - libcurl_la-cf-ip-happy.lo libcurl_la-cf-socket.lo \ + libcurl_la-cf-ip-happy.lo libcurl_la-cf-recvbuf.lo \ + libcurl_la-cf-setup.lo libcurl_la-cf-socket.lo \ libcurl_la-cfilters.lo libcurl_la-conncache.lo \ libcurl_la-connect.lo libcurl_la-content_encoding.lo \ - libcurl_la-cookie.lo libcurl_la-cshutdn.lo \ + libcurl_la-cookie.lo libcurl_la-creds.lo libcurl_la-cshutdn.lo \ libcurl_la-curl_addrinfo.lo libcurl_la-curl_endian.lo \ libcurl_la-curl_fnmatch.lo libcurl_la-curl_fopen.lo \ libcurl_la-curl_get_line.lo libcurl_la-curl_gethostname.lo \ @@ -334,20 +341,20 @@ am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \ libcurl_la-memdebug.lo libcurl_la-mime.lo \ libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \ libcurl_la-multi_ev.lo libcurl_la-multi_ntfy.lo \ - libcurl_la-netrc.lo libcurl_la-noproxy.lo \ - libcurl_la-openldap.lo libcurl_la-parsedate.lo \ + libcurl_la-netrc.lo libcurl_la-openldap.lo \ + libcurl_la-parsedate.lo libcurl_la-peer.lo \ libcurl_la-pingpong.lo libcurl_la-pop3.lo \ libcurl_la-progress.lo libcurl_la-protocol.lo \ - libcurl_la-psl.lo libcurl_la-rand.lo libcurl_la-ratelimit.lo \ - libcurl_la-request.lo libcurl_la-rtsp.lo libcurl_la-select.lo \ - libcurl_la-sendf.lo libcurl_la-setopt.lo libcurl_la-sha256.lo \ - libcurl_la-slist.lo libcurl_la-smb.lo libcurl_la-smtp.lo \ - libcurl_la-socketpair.lo libcurl_la-socks.lo \ - libcurl_la-socks_gssapi.lo libcurl_la-socks_sspi.lo \ - libcurl_la-splay.lo libcurl_la-strcase.lo \ - libcurl_la-strequal.lo libcurl_la-strerror.lo \ - libcurl_la-system_win32.lo libcurl_la-telnet.lo \ - libcurl_la-tftp.lo libcurl_la-thrdpool.lo \ + libcurl_la-proxy.lo libcurl_la-psl.lo libcurl_la-rand.lo \ + libcurl_la-ratelimit.lo libcurl_la-request.lo \ + libcurl_la-rtsp.lo libcurl_la-select.lo libcurl_la-sendf.lo \ + libcurl_la-setopt.lo libcurl_la-sha256.lo libcurl_la-slist.lo \ + libcurl_la-smb.lo libcurl_la-smtp.lo libcurl_la-socketpair.lo \ + libcurl_la-socks.lo libcurl_la-socks_gssapi.lo \ + libcurl_la-socks_sspi.lo libcurl_la-splay.lo \ + libcurl_la-strcase.lo libcurl_la-strequal.lo \ + libcurl_la-strerror.lo libcurl_la-system_win32.lo \ + libcurl_la-telnet.lo libcurl_la-tftp.lo libcurl_la-thrdpool.lo \ libcurl_la-thrdqueue.lo libcurl_la-transfer.lo \ libcurl_la-uint-bset.lo libcurl_la-uint-hash.lo \ libcurl_la-uint-spbset.lo libcurl_la-uint-table.lo \ @@ -367,10 +374,14 @@ am__objects_3 = vtls/libcurl_la-apple.lo \ vtls/libcurl_la-mbedtls.lo vtls/libcurl_la-openssl.lo \ vtls/libcurl_la-rustls.lo vtls/libcurl_la-schannel.lo \ vtls/libcurl_la-schannel_verify.lo vtls/libcurl_la-vtls.lo \ - vtls/libcurl_la-vtls_scache.lo vtls/libcurl_la-vtls_spack.lo \ - vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo -am__objects_4 = vquic/libcurl_la-curl_ngtcp2.lo \ - vquic/libcurl_la-curl_quiche.lo vquic/libcurl_la-vquic.lo \ + vtls/libcurl_la-vtls_config.lo vtls/libcurl_la-vtls_scache.lo \ + vtls/libcurl_la-vtls_spack.lo vtls/libcurl_la-wolfssl.lo \ + vtls/libcurl_la-x509asn1.lo +am__objects_4 = vquic/libcurl_la-capsule.lo \ + vquic/libcurl_la-cf-capsule.lo vquic/libcurl_la-cf-ngtcp2.lo \ + vquic/libcurl_la-cf-ngtcp2-cmn.lo \ + vquic/libcurl_la-cf-ngtcp2-proxy.lo \ + vquic/libcurl_la-cf-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-vssh.lo @@ -411,39 +422,42 @@ libcurlu_la_LIBADD = am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-dns.c \ cf-h1-proxy.c cf-h2-proxy.c cf-haproxy.c cf-https-connect.c \ - cf-ip-happy.c cf-socket.c cfilters.c conncache.c connect.c \ - content_encoding.c cookie.c cshutdn.c curl_addrinfo.c \ - curl_endian.c curl_fnmatch.c curl_fopen.c curl_get_line.c \ - curl_gethostname.c curl_gssapi.c curl_memrchr.c \ - curl_ntlm_core.c curl_range.c curl_sasl.c curl_sha512_256.c \ - curl_share.c curl_sspi.c curl_threads.c curl_trc.c cw-out.c \ - cw-pause.c dict.c dnscache.c doh.c dynhds.c easy.c \ - easygetopt.c easyoptions.c escape.c fake_addrinfo.c file.c \ - fileinfo.c formdata.c ftp.c ftplistparser.c getenv.c getinfo.c \ - gopher.c hash.c headers.c hmac.c hostip.c hostip4.c hostip6.c \ - hsts.c http.c http1.c http2.c http_aws_sigv4.c http_chunks.c \ - http_digest.c http_negotiate.c http_ntlm.c http_proxy.c \ - httpsrr.c idn.c if2ip.c imap.c ldap.c llist.c macos.c md4.c \ - md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \ - multi_ntfy.c netrc.c noproxy.c openldap.c parsedate.c \ - pingpong.c pop3.c progress.c protocol.c psl.c rand.c \ - ratelimit.c request.c rtsp.c select.c sendf.c setopt.c \ - sha256.c slist.c smb.c smtp.c socketpair.c socks.c \ - socks_gssapi.c socks_sspi.c splay.c strcase.c strequal.c \ - strerror.c system_win32.c telnet.c tftp.c thrdpool.c \ - thrdqueue.c transfer.c uint-bset.c uint-hash.c uint-spbset.c \ - uint-table.c url.c urlapi.c version.c ws.c vauth/cleartext.c \ - vauth/cram.c vauth/digest.c vauth/digest_sspi.c vauth/gsasl.c \ - vauth/krb5_gssapi.c vauth/krb5_sspi.c vauth/ntlm.c \ - vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_gssapi.c \ - vauth/spnego_sspi.c vauth/vauth.c vtls/apple.c \ - vtls/cipher_suite.c vtls/gtls.c vtls/hostcheck.c vtls/keylog.c \ - vtls/mbedtls.c vtls/openssl.c vtls/rustls.c vtls/schannel.c \ - vtls/schannel_verify.c vtls/vtls.c vtls/vtls_scache.c \ + cf-ip-happy.c cf-recvbuf.c cf-setup.c cf-socket.c cfilters.c \ + conncache.c connect.c content_encoding.c cookie.c creds.c \ + cshutdn.c curl_addrinfo.c curl_endian.c curl_fnmatch.c \ + curl_fopen.c curl_get_line.c curl_gethostname.c curl_gssapi.c \ + curl_memrchr.c curl_ntlm_core.c curl_range.c curl_sasl.c \ + curl_sha512_256.c curl_share.c curl_sspi.c curl_threads.c \ + curl_trc.c cw-out.c cw-pause.c dict.c dnscache.c doh.c \ + dynhds.c easy.c easygetopt.c easyoptions.c escape.c \ + fake_addrinfo.c file.c fileinfo.c formdata.c ftp.c \ + ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \ + hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \ + http2.c http_aws_sigv4.c http_chunks.c http_digest.c \ + http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \ + if2ip.c imap.c ldap.c llist.c macos.c md4.c md5.c memdebug.c \ + mime.c mprintf.c mqtt.c multi.c multi_ev.c multi_ntfy.c \ + netrc.c openldap.c parsedate.c peer.c pingpong.c pop3.c \ + progress.c protocol.c proxy.c psl.c rand.c ratelimit.c \ + request.c rtsp.c select.c sendf.c setopt.c sha256.c slist.c \ + smb.c smtp.c socketpair.c socks.c socks_gssapi.c socks_sspi.c \ + splay.c strcase.c strequal.c strerror.c system_win32.c \ + telnet.c tftp.c thrdpool.c thrdqueue.c transfer.c uint-bset.c \ + uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \ + version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \ + vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \ + vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \ + vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \ + vauth/vauth.c vtls/apple.c vtls/cipher_suite.c vtls/gtls.c \ + vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c vtls/openssl.c \ + vtls/rustls.c vtls/schannel.c vtls/schannel_verify.c \ + vtls/vtls.c vtls/vtls_config.c vtls/vtls_scache.c \ vtls/vtls_spack.c vtls/wolfssl.c vtls/x509asn1.c \ - vquic/curl_ngtcp2.c vquic/curl_quiche.c vquic/vquic.c \ - vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c vssh/vssh.c \ - curlx/base64.c curlx/basename.c curlx/dynbuf.c curlx/fopen.c \ + vquic/capsule.c vquic/cf-capsule.c vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c vquic/vquic.c vquic/vquic-tls.c \ + vssh/libssh.c vssh/libssh2.c vssh/vssh.c curlx/base64.c \ + curlx/basename.c curlx/dynbuf.c curlx/fopen.c \ curlx/inet_ntop.c curlx/inet_pton.c curlx/multibyte.c \ curlx/nonblock.c curlx/snprintf.c curlx/strcopy.c \ curlx/strdup.c curlx/strerr.c curlx/strparse.c \ @@ -451,11 +465,12 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ curlx/wait.c curlx/warnless.c curlx/winapi.c dllmain.c \ altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h bufref.h \ cf-dns.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \ - cf-https-connect.h cf-ip-happy.h cf-socket.h cfilters.h \ - conncache.h cshutdn.h connect.h content_encoding.h cookie.h \ - curl_addrinfo.h curl_ctype.h curl_endian.h curl_fnmatch.h \ - curl_fopen.h curl_get_line.h curl_gethostname.h curl_gssapi.h \ - curl_hmac.h curl_ldap.h curl_md4.h curl_md5.h curl_memrchr.h \ + cf-https-connect.h cf-ip-happy.h cf-recvbuf.h cf-setup.h \ + cf-socket.h cfilters.h conncache.h cshutdn.h connect.h \ + content_encoding.h cookie.h creds.h curl_addrinfo.h \ + curl_ctype.h curl_endian.h curl_fnmatch.h curl_fopen.h \ + curl_get_line.h curl_gethostname.h curl_gssapi.h curl_hmac.h \ + curl_ldap.h curl_md4.h curl_md5.h curl_memrchr.h \ curl_ntlm_core.h curl_printf.h curl_range.h curl_sasl.h \ curl_setup.h curl_sha256.h curl_sha512_256.h curl_share.h \ curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \ @@ -466,9 +481,9 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ http1.h http2.h http_aws_sigv4.h http_chunks.h http_digest.h \ http_negotiate.h http_ntlm.h http_proxy.h httpsrr.h idn.h \ if2ip.h imap.h llist.h macos.h mime.h mqtt.h multihandle.h \ - multi_ev.h multi_ntfy.h multiif.h netrc.h noproxy.h \ - parsedate.h pingpong.h pop3.h progress.h protocol.h psl.h \ - rand.h ratelimit.h request.h rtsp.h select.h sendf.h setopt.h \ + multi_ev.h multi_ntfy.h multiif.h netrc.h parsedate.h peer.h \ + pingpong.h pop3.h progress.h protocol.h proxy.h psl.h rand.h \ + ratelimit.h request.h rtsp.h select.h sendf.h setopt.h \ setup-os400.h setup-vms.h setup-win32.h sigpipe.h slist.h \ smb.h smtp.h sockaddr.h socketpair.h socks.h splay.h strcase.h \ strerror.h system_win32.h telnet.h tftp.h thrdpool.h \ @@ -477,9 +492,11 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ vauth/vauth.h vtls/apple.h vtls/cipher_suite.h vtls/gtls.h \ vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h vtls/openssl.h \ vtls/rustls.h vtls/schannel.h vtls/schannel_int.h vtls/vtls.h \ - vtls/vtls_int.h vtls/vtls_scache.h vtls/vtls_spack.h \ - vtls/wolfssl.h vtls/x509asn1.h vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \ + vtls/vtls_config.h vtls/vtls_int.h vtls/vtls_scache.h \ + vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \ + vquic/capsule.h vquic/cf-capsule.h vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h vquic/vquic.h vquic/vquic_int.h \ vquic/vquic-tls.h vssh/vssh.h vssh/ssh.h curlx/base64.h \ curlx/basename.h curlx/dynbuf.h curlx/fopen.h \ curlx/inet_ntop.h curlx/inet_pton.h curlx/multibyte.h \ @@ -493,20 +510,22 @@ am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \ libcurlu_la-bufref.lo libcurlu_la-cf-dns.lo \ libcurlu_la-cf-h1-proxy.lo libcurlu_la-cf-h2-proxy.lo \ libcurlu_la-cf-haproxy.lo libcurlu_la-cf-https-connect.lo \ - libcurlu_la-cf-ip-happy.lo libcurlu_la-cf-socket.lo \ + libcurlu_la-cf-ip-happy.lo libcurlu_la-cf-recvbuf.lo \ + libcurlu_la-cf-setup.lo libcurlu_la-cf-socket.lo \ libcurlu_la-cfilters.lo libcurlu_la-conncache.lo \ libcurlu_la-connect.lo libcurlu_la-content_encoding.lo \ - libcurlu_la-cookie.lo libcurlu_la-cshutdn.lo \ - libcurlu_la-curl_addrinfo.lo libcurlu_la-curl_endian.lo \ - libcurlu_la-curl_fnmatch.lo libcurlu_la-curl_fopen.lo \ - libcurlu_la-curl_get_line.lo libcurlu_la-curl_gethostname.lo \ - libcurlu_la-curl_gssapi.lo libcurlu_la-curl_memrchr.lo \ - libcurlu_la-curl_ntlm_core.lo libcurlu_la-curl_range.lo \ - libcurlu_la-curl_sasl.lo libcurlu_la-curl_sha512_256.lo \ - libcurlu_la-curl_share.lo libcurlu_la-curl_sspi.lo \ - libcurlu_la-curl_threads.lo libcurlu_la-curl_trc.lo \ - libcurlu_la-cw-out.lo libcurlu_la-cw-pause.lo \ - libcurlu_la-dict.lo libcurlu_la-dnscache.lo libcurlu_la-doh.lo \ + libcurlu_la-cookie.lo libcurlu_la-creds.lo \ + libcurlu_la-cshutdn.lo libcurlu_la-curl_addrinfo.lo \ + libcurlu_la-curl_endian.lo libcurlu_la-curl_fnmatch.lo \ + libcurlu_la-curl_fopen.lo libcurlu_la-curl_get_line.lo \ + libcurlu_la-curl_gethostname.lo libcurlu_la-curl_gssapi.lo \ + libcurlu_la-curl_memrchr.lo libcurlu_la-curl_ntlm_core.lo \ + libcurlu_la-curl_range.lo libcurlu_la-curl_sasl.lo \ + libcurlu_la-curl_sha512_256.lo libcurlu_la-curl_share.lo \ + libcurlu_la-curl_sspi.lo libcurlu_la-curl_threads.lo \ + libcurlu_la-curl_trc.lo libcurlu_la-cw-out.lo \ + libcurlu_la-cw-pause.lo libcurlu_la-dict.lo \ + libcurlu_la-dnscache.lo libcurlu_la-doh.lo \ libcurlu_la-dynhds.lo libcurlu_la-easy.lo \ libcurlu_la-easygetopt.lo libcurlu_la-easyoptions.lo \ libcurlu_la-escape.lo libcurlu_la-fake_addrinfo.lo \ @@ -528,10 +547,11 @@ am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \ libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \ libcurlu_la-multi.lo libcurlu_la-multi_ev.lo \ libcurlu_la-multi_ntfy.lo libcurlu_la-netrc.lo \ - libcurlu_la-noproxy.lo libcurlu_la-openldap.lo \ - libcurlu_la-parsedate.lo libcurlu_la-pingpong.lo \ + libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \ + libcurlu_la-peer.lo libcurlu_la-pingpong.lo \ libcurlu_la-pop3.lo libcurlu_la-progress.lo \ - libcurlu_la-protocol.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \ + libcurlu_la-protocol.lo libcurlu_la-proxy.lo \ + libcurlu_la-psl.lo libcurlu_la-rand.lo \ libcurlu_la-ratelimit.lo libcurlu_la-request.lo \ libcurlu_la-rtsp.lo libcurlu_la-select.lo libcurlu_la-sendf.lo \ libcurlu_la-setopt.lo libcurlu_la-sha256.lo \ @@ -561,10 +581,14 @@ am__objects_15 = vtls/libcurlu_la-apple.lo \ vtls/libcurlu_la-mbedtls.lo vtls/libcurlu_la-openssl.lo \ vtls/libcurlu_la-rustls.lo vtls/libcurlu_la-schannel.lo \ vtls/libcurlu_la-schannel_verify.lo vtls/libcurlu_la-vtls.lo \ + vtls/libcurlu_la-vtls_config.lo \ vtls/libcurlu_la-vtls_scache.lo vtls/libcurlu_la-vtls_spack.lo \ vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo -am__objects_16 = vquic/libcurlu_la-curl_ngtcp2.lo \ - vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo \ +am__objects_16 = vquic/libcurlu_la-capsule.lo \ + vquic/libcurlu_la-cf-capsule.lo vquic/libcurlu_la-cf-ngtcp2.lo \ + vquic/libcurlu_la-cf-ngtcp2-cmn.lo \ + vquic/libcurlu_la-cf-ngtcp2-proxy.lo \ + vquic/libcurlu_la-cf-quiche.lo vquic/libcurlu_la-vquic.lo \ vquic/libcurlu_la-vquic-tls.lo am__objects_17 = vssh/libcurlu_la-libssh.lo \ vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-vssh.lo @@ -620,12 +644,15 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo \ ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo \ ./$(DEPDIR)/libcurl_la-cf-ip-happy.Plo \ + ./$(DEPDIR)/libcurl_la-cf-recvbuf.Plo \ + ./$(DEPDIR)/libcurl_la-cf-setup.Plo \ ./$(DEPDIR)/libcurl_la-cf-socket.Plo \ ./$(DEPDIR)/libcurl_la-cfilters.Plo \ ./$(DEPDIR)/libcurl_la-conncache.Plo \ ./$(DEPDIR)/libcurl_la-connect.Plo \ ./$(DEPDIR)/libcurl_la-content_encoding.Plo \ ./$(DEPDIR)/libcurl_la-cookie.Plo \ + ./$(DEPDIR)/libcurl_la-creds.Plo \ ./$(DEPDIR)/libcurl_la-cshutdn.Plo \ ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \ ./$(DEPDIR)/libcurl_la-curl_endian.Plo \ @@ -696,13 +723,14 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ ./$(DEPDIR)/libcurl_la-multi_ev.Plo \ ./$(DEPDIR)/libcurl_la-multi_ntfy.Plo \ ./$(DEPDIR)/libcurl_la-netrc.Plo \ - ./$(DEPDIR)/libcurl_la-noproxy.Plo \ ./$(DEPDIR)/libcurl_la-openldap.Plo \ ./$(DEPDIR)/libcurl_la-parsedate.Plo \ + ./$(DEPDIR)/libcurl_la-peer.Plo \ ./$(DEPDIR)/libcurl_la-pingpong.Plo \ ./$(DEPDIR)/libcurl_la-pop3.Plo \ ./$(DEPDIR)/libcurl_la-progress.Plo \ ./$(DEPDIR)/libcurl_la-protocol.Plo \ + ./$(DEPDIR)/libcurl_la-proxy.Plo \ ./$(DEPDIR)/libcurl_la-psl.Plo ./$(DEPDIR)/libcurl_la-rand.Plo \ ./$(DEPDIR)/libcurl_la-ratelimit.Plo \ ./$(DEPDIR)/libcurl_la-request.Plo \ @@ -748,12 +776,15 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo \ ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo \ ./$(DEPDIR)/libcurlu_la-cf-ip-happy.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-recvbuf.Plo \ + ./$(DEPDIR)/libcurlu_la-cf-setup.Plo \ ./$(DEPDIR)/libcurlu_la-cf-socket.Plo \ ./$(DEPDIR)/libcurlu_la-cfilters.Plo \ ./$(DEPDIR)/libcurlu_la-conncache.Plo \ ./$(DEPDIR)/libcurlu_la-connect.Plo \ ./$(DEPDIR)/libcurlu_la-content_encoding.Plo \ ./$(DEPDIR)/libcurlu_la-cookie.Plo \ + ./$(DEPDIR)/libcurlu_la-creds.Plo \ ./$(DEPDIR)/libcurlu_la-cshutdn.Plo \ ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \ ./$(DEPDIR)/libcurlu_la-curl_endian.Plo \ @@ -825,13 +856,14 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ ./$(DEPDIR)/libcurlu_la-multi_ev.Plo \ ./$(DEPDIR)/libcurlu_la-multi_ntfy.Plo \ ./$(DEPDIR)/libcurlu_la-netrc.Plo \ - ./$(DEPDIR)/libcurlu_la-noproxy.Plo \ ./$(DEPDIR)/libcurlu_la-openldap.Plo \ ./$(DEPDIR)/libcurlu_la-parsedate.Plo \ + ./$(DEPDIR)/libcurlu_la-peer.Plo \ ./$(DEPDIR)/libcurlu_la-pingpong.Plo \ ./$(DEPDIR)/libcurlu_la-pop3.Plo \ ./$(DEPDIR)/libcurlu_la-progress.Plo \ ./$(DEPDIR)/libcurlu_la-protocol.Plo \ + ./$(DEPDIR)/libcurlu_la-proxy.Plo \ ./$(DEPDIR)/libcurlu_la-psl.Plo \ ./$(DEPDIR)/libcurlu_la-rand.Plo \ ./$(DEPDIR)/libcurlu_la-ratelimit.Plo \ @@ -930,12 +962,20 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo \ vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo \ vauth/$(DEPDIR)/libcurlu_la-vauth.Plo \ - vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo \ - vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo \ + vquic/$(DEPDIR)/libcurl_la-capsule.Plo \ + vquic/$(DEPDIR)/libcurl_la-cf-capsule.Plo \ + vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Plo \ + vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Plo \ + vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Plo \ + vquic/$(DEPDIR)/libcurl_la-cf-quiche.Plo \ vquic/$(DEPDIR)/libcurl_la-vquic-tls.Plo \ vquic/$(DEPDIR)/libcurl_la-vquic.Plo \ - vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo \ - vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo \ + vquic/$(DEPDIR)/libcurlu_la-capsule.Plo \ + vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Plo \ + vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Plo \ + vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Plo \ + vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Plo \ + vquic/$(DEPDIR)/libcurlu_la-cf-quiche.Plo \ vquic/$(DEPDIR)/libcurlu_la-vquic-tls.Plo \ vquic/$(DEPDIR)/libcurlu_la-vquic.Plo \ vssh/$(DEPDIR)/libcurl_la-libssh.Plo \ @@ -955,6 +995,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ vtls/$(DEPDIR)/libcurl_la-schannel.Plo \ vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo \ vtls/$(DEPDIR)/libcurl_la-vtls.Plo \ + vtls/$(DEPDIR)/libcurl_la-vtls_config.Plo \ vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo \ vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo \ vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo \ @@ -970,6 +1011,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ vtls/$(DEPDIR)/libcurlu_la-schannel.Plo \ vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo \ vtls/$(DEPDIR)/libcurlu_la-vtls.Plo \ + vtls/$(DEPDIR)/libcurlu_la-vtls_config.Plo \ vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo \ vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo \ vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo \ @@ -1077,6 +1119,7 @@ FILECMD = @FILECMD@ FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ GCOV = @GCOV@ GREP = @GREP@ +H2O = @H2O@ HAVE_LIBZ = @HAVE_LIBZ@ HTTPD = @HTTPD@ HTTPD_NGHTTPX = @HTTPD_NGHTTPX@ @@ -1293,6 +1336,7 @@ LIB_VTLS_CFILES = \ vtls/schannel.c \ vtls/schannel_verify.c \ vtls/vtls.c \ + vtls/vtls_config.c \ vtls/vtls_scache.c \ vtls/vtls_spack.c \ vtls/wolfssl.c \ @@ -1310,6 +1354,7 @@ LIB_VTLS_HFILES = \ vtls/schannel.h \ vtls/schannel_int.h \ vtls/vtls.h \ + vtls/vtls_config.h \ vtls/vtls_int.h \ vtls/vtls_scache.h \ vtls/vtls_spack.h \ @@ -1317,16 +1362,24 @@ LIB_VTLS_HFILES = \ vtls/x509asn1.h LIB_VQUIC_CFILES = \ - vquic/curl_ngtcp2.c \ - vquic/curl_quiche.c \ - vquic/vquic.c \ + vquic/capsule.c \ + vquic/cf-capsule.c \ + vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c \ + vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c \ + vquic/vquic.c \ vquic/vquic-tls.c LIB_VQUIC_HFILES = \ - vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ + vquic/capsule.h \ + vquic/cf-capsule.h \ + vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h \ + vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h LIB_VSSH_CFILES = \ @@ -1352,12 +1405,15 @@ LIB_CFILES = \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ + cf-recvbuf.c \ + cf-setup.c \ cf-socket.c \ cfilters.c \ conncache.c \ connect.c \ content_encoding.c \ cookie.c \ + creds.c \ cshutdn.c \ curl_addrinfo.c \ curl_endian.c \ @@ -1427,13 +1483,14 @@ LIB_CFILES = \ multi_ev.c \ multi_ntfy.c \ netrc.c \ - noproxy.c \ openldap.c \ parsedate.c \ + peer.c \ pingpong.c \ pop3.c \ progress.c \ protocol.c \ + proxy.c \ psl.c \ rand.c \ ratelimit.c \ @@ -1482,6 +1539,8 @@ LIB_HFILES = \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ + cf-recvbuf.h \ + cf-setup.h \ cf-socket.h \ cfilters.h \ conncache.h \ @@ -1489,6 +1548,7 @@ LIB_HFILES = \ connect.h \ content_encoding.h \ cookie.h \ + creds.h \ curl_addrinfo.h \ curl_ctype.h \ curl_endian.h \ @@ -1559,12 +1619,13 @@ LIB_HFILES = \ multi_ntfy.h \ multiif.h \ netrc.h \ - noproxy.h \ parsedate.h \ + peer.h \ pingpong.h \ pop3.h \ progress.h \ protocol.h \ + proxy.h \ psl.h \ rand.h \ ratelimit.h \ @@ -1830,6 +1891,8 @@ vtls/libcurl_la-schannel_verify.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurl_la-vtls.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurl_la-vtls_config.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurl_la-vtls_scache.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurl_la-vtls_spack.lo: vtls/$(am__dirstamp) \ @@ -1844,9 +1907,17 @@ vquic/$(am__dirstamp): vquic/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) vquic/$(DEPDIR) @: > vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \ +vquic/libcurl_la-capsule.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-cf-capsule.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-cf-ngtcp2.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-cf-ngtcp2-cmn.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-curl_quiche.lo: vquic/$(am__dirstamp) \ +vquic/libcurl_la-cf-ngtcp2-proxy.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurl_la-cf-quiche.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) vquic/libcurl_la-vquic.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) @@ -1959,6 +2030,8 @@ vtls/libcurlu_la-schannel_verify.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurlu_la-vtls.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) +vtls/libcurlu_la-vtls_config.lo: vtls/$(am__dirstamp) \ + vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurlu_la-vtls_scache.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurlu_la-vtls_spack.lo: vtls/$(am__dirstamp) \ @@ -1967,9 +2040,17 @@ vtls/libcurlu_la-wolfssl.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) vtls/libcurlu_la-x509asn1.lo: vtls/$(am__dirstamp) \ vtls/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-curl_ngtcp2.lo: vquic/$(am__dirstamp) \ +vquic/libcurlu_la-capsule.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-cf-capsule.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-curl_quiche.lo: vquic/$(am__dirstamp) \ +vquic/libcurlu_la-cf-ngtcp2.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-cf-ngtcp2-cmn.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-cf-ngtcp2-proxy.lo: vquic/$(am__dirstamp) \ + vquic/$(DEPDIR)/$(am__dirstamp) +vquic/libcurlu_la-cf-quiche.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) vquic/libcurlu_la-vquic.lo: vquic/$(am__dirstamp) \ vquic/$(DEPDIR)/$(am__dirstamp) @@ -2052,12 +2133,15 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-haproxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-https-connect.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-ip-happy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-recvbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-setup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-socket.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cfilters.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-conncache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-creds.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cshutdn.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker @@ -2129,13 +2213,14 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi_ev.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi_ntfy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-peer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pingpong.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pop3.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-progress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-protocol.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-proxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-psl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ratelimit.Plo@am__quote@ # am--include-marker @@ -2183,12 +2268,15 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-ip-happy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-recvbuf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-setup.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-socket.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cfilters.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-conncache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-creds.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cshutdn.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker @@ -2260,13 +2348,14 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi_ev.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi_ntfy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-peer.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pingpong.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pop3.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-progress.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-protocol.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-proxy.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-psl.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ratelimit.Plo@am__quote@ # am--include-marker @@ -2365,12 +2454,20 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo@am__quote@ # am--include-marker @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_ngtcp2.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-capsule.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-cf-capsule.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-cf-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_ngtcp2.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-capsule.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-cf-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 @@ -2390,6 +2487,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls_config.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo@am__quote@ # am--include-marker @@ -2405,6 +2503,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls_config.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo@am__quote@ # am--include-marker @@ -2531,6 +2630,20 @@ libcurl_la-cf-ip-happy.lo: cf-ip-happy.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 libcurl_la-cf-ip-happy.lo `test -f 'cf-ip-happy.c' || echo '$(srcdir)/'`cf-ip-happy.c +libcurl_la-cf-recvbuf.lo: cf-recvbuf.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 libcurl_la-cf-recvbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-recvbuf.Tpo -c -o libcurl_la-cf-recvbuf.lo `test -f 'cf-recvbuf.c' || echo '$(srcdir)/'`cf-recvbuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-recvbuf.Tpo $(DEPDIR)/libcurl_la-cf-recvbuf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-recvbuf.c' object='libcurl_la-cf-recvbuf.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 libcurl_la-cf-recvbuf.lo `test -f 'cf-recvbuf.c' || echo '$(srcdir)/'`cf-recvbuf.c + +libcurl_la-cf-setup.lo: cf-setup.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 libcurl_la-cf-setup.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-setup.Tpo -c -o libcurl_la-cf-setup.lo `test -f 'cf-setup.c' || echo '$(srcdir)/'`cf-setup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-setup.Tpo $(DEPDIR)/libcurl_la-cf-setup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-setup.c' object='libcurl_la-cf-setup.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 libcurl_la-cf-setup.lo `test -f 'cf-setup.c' || echo '$(srcdir)/'`cf-setup.c + libcurl_la-cf-socket.lo: cf-socket.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 libcurl_la-cf-socket.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-socket.Tpo -c -o libcurl_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-socket.Tpo $(DEPDIR)/libcurl_la-cf-socket.Plo @@ -2573,6 +2686,13 @@ libcurl_la-cookie.lo: cookie.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 libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c +libcurl_la-creds.lo: creds.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 libcurl_la-creds.lo -MD -MP -MF $(DEPDIR)/libcurl_la-creds.Tpo -c -o libcurl_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-creds.Tpo $(DEPDIR)/libcurl_la-creds.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='creds.c' object='libcurl_la-creds.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 libcurl_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c + libcurl_la-cshutdn.lo: cshutdn.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 libcurl_la-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cshutdn.Tpo -c -o libcurl_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cshutdn.Tpo $(DEPDIR)/libcurl_la-cshutdn.Plo @@ -3056,13 +3176,6 @@ libcurl_la-netrc.lo: netrc.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 libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c -libcurl_la-noproxy.lo: noproxy.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 libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurl_la-noproxy.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 libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c - libcurl_la-openldap.lo: openldap.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 libcurl_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-openldap.Tpo -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-openldap.Tpo $(DEPDIR)/libcurl_la-openldap.Plo @@ -3077,6 +3190,13 @@ libcurl_la-parsedate.lo: parsedate.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 libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c +libcurl_la-peer.lo: peer.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 libcurl_la-peer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-peer.Tpo -c -o libcurl_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-peer.Tpo $(DEPDIR)/libcurl_la-peer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='peer.c' object='libcurl_la-peer.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 libcurl_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c + libcurl_la-pingpong.lo: pingpong.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 libcurl_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pingpong.Tpo -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pingpong.Tpo $(DEPDIR)/libcurl_la-pingpong.Plo @@ -3105,6 +3225,13 @@ libcurl_la-protocol.lo: protocol.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 libcurl_la-protocol.lo `test -f 'protocol.c' || echo '$(srcdir)/'`protocol.c +libcurl_la-proxy.lo: proxy.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 libcurl_la-proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-proxy.Tpo -c -o libcurl_la-proxy.lo `test -f 'proxy.c' || echo '$(srcdir)/'`proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-proxy.Tpo $(DEPDIR)/libcurl_la-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='proxy.c' object='libcurl_la-proxy.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 libcurl_la-proxy.lo `test -f 'proxy.c' || echo '$(srcdir)/'`proxy.c + libcurl_la-psl.lo: psl.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 libcurl_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-psl.Tpo -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-psl.Tpo $(DEPDIR)/libcurl_la-psl.Plo @@ -3511,6 +3638,13 @@ vtls/libcurl_la-vtls.lo: vtls/vtls.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 vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c +vtls/libcurl_la-vtls_config.lo: vtls/vtls_config.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 vtls/libcurl_la-vtls_config.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls_config.Tpo -c -o vtls/libcurl_la-vtls_config.lo `test -f 'vtls/vtls_config.c' || echo '$(srcdir)/'`vtls/vtls_config.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls_config.Tpo vtls/$(DEPDIR)/libcurl_la-vtls_config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_config.c' object='vtls/libcurl_la-vtls_config.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 vtls/libcurl_la-vtls_config.lo `test -f 'vtls/vtls_config.c' || echo '$(srcdir)/'`vtls/vtls_config.c + vtls/libcurl_la-vtls_scache.lo: vtls/vtls_scache.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 vtls/libcurl_la-vtls_scache.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls_scache.Tpo -c -o vtls/libcurl_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls_scache.Tpo vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo @@ -3539,19 +3673,47 @@ vtls/libcurl_la-x509asn1.lo: vtls/x509asn1.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 vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c -vquic/libcurl_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.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_ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Tpo -c -o vquic/libcurl_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Tpo vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_ngtcp2.c' object='vquic/libcurl_la-curl_ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ +vquic/libcurl_la-capsule.lo: vquic/capsule.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-capsule.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-capsule.Tpo -c -o vquic/libcurl_la-capsule.lo `test -f 'vquic/capsule.c' || echo '$(srcdir)/'`vquic/capsule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-capsule.Tpo vquic/$(DEPDIR)/libcurl_la-capsule.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/capsule.c' object='vquic/libcurl_la-capsule.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-capsule.lo `test -f 'vquic/capsule.c' || echo '$(srcdir)/'`vquic/capsule.c + +vquic/libcurl_la-cf-capsule.lo: vquic/cf-capsule.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-cf-capsule.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-cf-capsule.Tpo -c -o vquic/libcurl_la-cf-capsule.lo `test -f 'vquic/cf-capsule.c' || echo '$(srcdir)/'`vquic/cf-capsule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-cf-capsule.Tpo vquic/$(DEPDIR)/libcurl_la-cf-capsule.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-capsule.c' object='vquic/libcurl_la-cf-capsule.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-cf-capsule.lo `test -f 'vquic/cf-capsule.c' || echo '$(srcdir)/'`vquic/cf-capsule.c + +vquic/libcurl_la-cf-ngtcp2.lo: vquic/cf-ngtcp2.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-cf-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Tpo -c -o vquic/libcurl_la-cf-ngtcp2.lo `test -f 'vquic/cf-ngtcp2.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Tpo vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2.c' object='vquic/libcurl_la-cf-ngtcp2.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-cf-ngtcp2.lo `test -f 'vquic/cf-ngtcp2.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2.c + +vquic/libcurl_la-cf-ngtcp2-cmn.lo: vquic/cf-ngtcp2-cmn.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-cf-ngtcp2-cmn.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Tpo -c -o vquic/libcurl_la-cf-ngtcp2-cmn.lo `test -f 'vquic/cf-ngtcp2-cmn.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-cmn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Tpo vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2-cmn.c' object='vquic/libcurl_la-cf-ngtcp2-cmn.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_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c +@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-cf-ngtcp2-cmn.lo `test -f 'vquic/cf-ngtcp2-cmn.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-cmn.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 -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_quiche.c' object='vquic/libcurl_la-curl_quiche.lo' libtool=yes @AMDEPBACKSLASH@ +vquic/libcurl_la-cf-ngtcp2-proxy.lo: vquic/cf-ngtcp2-proxy.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-cf-ngtcp2-proxy.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Tpo -c -o vquic/libcurl_la-cf-ngtcp2-proxy.lo `test -f 'vquic/cf-ngtcp2-proxy.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Tpo vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2-proxy.c' object='vquic/libcurl_la-cf-ngtcp2-proxy.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_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c +@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-cf-ngtcp2-proxy.lo `test -f 'vquic/cf-ngtcp2-proxy.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-proxy.c + +vquic/libcurl_la-cf-quiche.lo: vquic/cf-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-cf-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-cf-quiche.Tpo -c -o vquic/libcurl_la-cf-quiche.lo `test -f 'vquic/cf-quiche.c' || echo '$(srcdir)/'`vquic/cf-quiche.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-cf-quiche.Tpo vquic/$(DEPDIR)/libcurl_la-cf-quiche.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-quiche.c' object='vquic/libcurl_la-cf-quiche.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-cf-quiche.lo `test -f 'vquic/cf-quiche.c' || echo '$(srcdir)/'`vquic/cf-quiche.c vquic/libcurl_la-vquic.lo: vquic/vquic.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.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic.Tpo -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c @@ -3826,6 +3988,20 @@ libcurlu_la-cf-ip-happy.lo: cf-ip-happy.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-ip-happy.lo `test -f 'cf-ip-happy.c' || echo '$(srcdir)/'`cf-ip-happy.c +libcurlu_la-cf-recvbuf.lo: cf-recvbuf.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-recvbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-recvbuf.Tpo -c -o libcurlu_la-cf-recvbuf.lo `test -f 'cf-recvbuf.c' || echo '$(srcdir)/'`cf-recvbuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-recvbuf.Tpo $(DEPDIR)/libcurlu_la-cf-recvbuf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-recvbuf.c' object='libcurlu_la-cf-recvbuf.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-recvbuf.lo `test -f 'cf-recvbuf.c' || echo '$(srcdir)/'`cf-recvbuf.c + +libcurlu_la-cf-setup.lo: cf-setup.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-setup.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-setup.Tpo -c -o libcurlu_la-cf-setup.lo `test -f 'cf-setup.c' || echo '$(srcdir)/'`cf-setup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-setup.Tpo $(DEPDIR)/libcurlu_la-cf-setup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cf-setup.c' object='libcurlu_la-cf-setup.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cf-setup.lo `test -f 'cf-setup.c' || echo '$(srcdir)/'`cf-setup.c + libcurlu_la-cf-socket.lo: cf-socket.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-socket.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-socket.Tpo -c -o libcurlu_la-cf-socket.lo `test -f 'cf-socket.c' || echo '$(srcdir)/'`cf-socket.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-socket.Tpo $(DEPDIR)/libcurlu_la-cf-socket.Plo @@ -3868,6 +4044,13 @@ libcurlu_la-cookie.lo: cookie.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c +libcurlu_la-creds.lo: creds.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-creds.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-creds.Tpo -c -o libcurlu_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-creds.Tpo $(DEPDIR)/libcurlu_la-creds.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='creds.c' object='libcurlu_la-creds.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c + libcurlu_la-cshutdn.lo: cshutdn.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cshutdn.Tpo -c -o libcurlu_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cshutdn.Tpo $(DEPDIR)/libcurlu_la-cshutdn.Plo @@ -4351,13 +4534,6 @@ libcurlu_la-netrc.lo: netrc.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c -libcurlu_la-noproxy.lo: noproxy.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurlu_la-noproxy.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c - libcurlu_la-openldap.lo: openldap.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-openldap.Tpo -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-openldap.Tpo $(DEPDIR)/libcurlu_la-openldap.Plo @@ -4372,6 +4548,13 @@ libcurlu_la-parsedate.lo: parsedate.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c +libcurlu_la-peer.lo: peer.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-peer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-peer.Tpo -c -o libcurlu_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-peer.Tpo $(DEPDIR)/libcurlu_la-peer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='peer.c' object='libcurlu_la-peer.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c + libcurlu_la-pingpong.lo: pingpong.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pingpong.Tpo -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pingpong.Tpo $(DEPDIR)/libcurlu_la-pingpong.Plo @@ -4400,6 +4583,13 @@ libcurlu_la-protocol.lo: protocol.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-protocol.lo `test -f 'protocol.c' || echo '$(srcdir)/'`protocol.c +libcurlu_la-proxy.lo: proxy.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-proxy.Tpo -c -o libcurlu_la-proxy.lo `test -f 'proxy.c' || echo '$(srcdir)/'`proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-proxy.Tpo $(DEPDIR)/libcurlu_la-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='proxy.c' object='libcurlu_la-proxy.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) $(AM_CFLAGS) $(CFLAGS) -c -o libcurlu_la-proxy.lo `test -f 'proxy.c' || echo '$(srcdir)/'`proxy.c + libcurlu_la-psl.lo: psl.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) $(AM_CFLAGS) $(CFLAGS) -MT libcurlu_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-psl.Tpo -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-psl.Tpo $(DEPDIR)/libcurlu_la-psl.Plo @@ -4806,6 +4996,13 @@ vtls/libcurlu_la-vtls.lo: vtls/vtls.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) $(AM_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c +vtls/libcurlu_la-vtls_config.lo: vtls/vtls_config.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) $(AM_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls_config.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls_config.Tpo -c -o vtls/libcurlu_la-vtls_config.lo `test -f 'vtls/vtls_config.c' || echo '$(srcdir)/'`vtls/vtls_config.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls_config.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls_config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_config.c' object='vtls/libcurlu_la-vtls_config.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) $(AM_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls_config.lo `test -f 'vtls/vtls_config.c' || echo '$(srcdir)/'`vtls/vtls_config.c + vtls/libcurlu_la-vtls_scache.lo: vtls/vtls_scache.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) $(AM_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls_scache.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Tpo -c -o vtls/libcurlu_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo @@ -4834,19 +5031,47 @@ vtls/libcurlu_la-x509asn1.lo: vtls/x509asn1.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) $(AM_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c -vquic/libcurlu_la-curl_ngtcp2.lo: vquic/curl_ngtcp2.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-curl_ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Tpo -c -o vquic/libcurlu_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Tpo vquic/$(DEPDIR)/libcurlu_la-curl_ngtcp2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_ngtcp2.c' object='vquic/libcurlu_la-curl_ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ +vquic/libcurlu_la-capsule.lo: vquic/capsule.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-capsule.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-capsule.Tpo -c -o vquic/libcurlu_la-capsule.lo `test -f 'vquic/capsule.c' || echo '$(srcdir)/'`vquic/capsule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-capsule.Tpo vquic/$(DEPDIR)/libcurlu_la-capsule.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/capsule.c' object='vquic/libcurlu_la-capsule.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-capsule.lo `test -f 'vquic/capsule.c' || echo '$(srcdir)/'`vquic/capsule.c + +vquic/libcurlu_la-cf-capsule.lo: vquic/cf-capsule.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-cf-capsule.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Tpo -c -o vquic/libcurlu_la-cf-capsule.lo `test -f 'vquic/cf-capsule.c' || echo '$(srcdir)/'`vquic/cf-capsule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Tpo vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-capsule.c' object='vquic/libcurlu_la-cf-capsule.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-cf-capsule.lo `test -f 'vquic/cf-capsule.c' || echo '$(srcdir)/'`vquic/cf-capsule.c + +vquic/libcurlu_la-cf-ngtcp2.lo: vquic/cf-ngtcp2.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-cf-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Tpo -c -o vquic/libcurlu_la-cf-ngtcp2.lo `test -f 'vquic/cf-ngtcp2.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Tpo vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2.c' object='vquic/libcurlu_la-cf-ngtcp2.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-cf-ngtcp2.lo `test -f 'vquic/cf-ngtcp2.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2.c + +vquic/libcurlu_la-cf-ngtcp2-cmn.lo: vquic/cf-ngtcp2-cmn.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-cf-ngtcp2-cmn.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Tpo -c -o vquic/libcurlu_la-cf-ngtcp2-cmn.lo `test -f 'vquic/cf-ngtcp2-cmn.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-cmn.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Tpo vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2-cmn.c' object='vquic/libcurlu_la-cf-ngtcp2-cmn.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-cf-ngtcp2-cmn.lo `test -f 'vquic/cf-ngtcp2-cmn.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-cmn.c + +vquic/libcurlu_la-cf-ngtcp2-proxy.lo: vquic/cf-ngtcp2-proxy.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-cf-ngtcp2-proxy.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Tpo -c -o vquic/libcurlu_la-cf-ngtcp2-proxy.lo `test -f 'vquic/cf-ngtcp2-proxy.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Tpo vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-ngtcp2-proxy.c' object='vquic/libcurlu_la-cf-ngtcp2-proxy.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_ngtcp2.lo `test -f 'vquic/curl_ngtcp2.c' || echo '$(srcdir)/'`vquic/curl_ngtcp2.c +@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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-cf-ngtcp2-proxy.lo `test -f 'vquic/cf-ngtcp2-proxy.c' || echo '$(srcdir)/'`vquic/cf-ngtcp2-proxy.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) $(AM_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 -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/curl_quiche.c' object='vquic/libcurlu_la-curl_quiche.lo' libtool=yes @AMDEPBACKSLASH@ +vquic/libcurlu_la-cf-quiche.lo: vquic/cf-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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-cf-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-cf-quiche.Tpo -c -o vquic/libcurlu_la-cf-quiche.lo `test -f 'vquic/cf-quiche.c' || echo '$(srcdir)/'`vquic/cf-quiche.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-cf-quiche.Tpo vquic/$(DEPDIR)/libcurlu_la-cf-quiche.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/cf-quiche.c' object='vquic/libcurlu_la-cf-quiche.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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-curl_quiche.lo `test -f 'vquic/curl_quiche.c' || echo '$(srcdir)/'`vquic/curl_quiche.c +@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) $(AM_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-cf-quiche.lo `test -f 'vquic/cf-quiche.c' || echo '$(srcdir)/'`vquic/cf-quiche.c vquic/libcurlu_la-vquic.lo: vquic/vquic.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) $(AM_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c @@ -5192,12 +5417,15 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-ip-happy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-recvbuf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-setup.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-socket.Plo -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurl_la-creds.Plo -rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo @@ -5269,13 +5497,14 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo -rm -f ./$(DEPDIR)/libcurl_la-multi_ntfy.Plo -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-peer.Plo -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo -rm -f ./$(DEPDIR)/libcurl_la-protocol.Plo + -rm -f ./$(DEPDIR)/libcurl_la-proxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo -rm -f ./$(DEPDIR)/libcurl_la-ratelimit.Plo @@ -5323,12 +5552,15 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-ip-happy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-recvbuf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-setup.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-socket.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-creds.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo @@ -5400,13 +5632,14 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo -rm -f ./$(DEPDIR)/libcurlu_la-multi_ntfy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-peer.Plo -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo -rm -f ./$(DEPDIR)/libcurlu_la-protocol.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-proxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo -rm -f ./$(DEPDIR)/libcurlu_la-ratelimit.Plo @@ -5505,12 +5738,20 @@ distclean: distclean-am -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-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_ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-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 @@ -5530,6 +5771,7 @@ distclean: distclean-am -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_config.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo @@ -5545,6 +5787,7 @@ distclean: distclean-am -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_config.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo @@ -5607,12 +5850,15 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-https-connect.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-ip-happy.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-recvbuf.Plo + -rm -f ./$(DEPDIR)/libcurl_la-cf-setup.Plo -rm -f ./$(DEPDIR)/libcurl_la-cf-socket.Plo -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurl_la-creds.Plo -rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo @@ -5684,13 +5930,14 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo -rm -f ./$(DEPDIR)/libcurl_la-multi_ntfy.Plo -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurl_la-peer.Plo -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo -rm -f ./$(DEPDIR)/libcurl_la-protocol.Plo + -rm -f ./$(DEPDIR)/libcurl_la-proxy.Plo -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo -rm -f ./$(DEPDIR)/libcurl_la-ratelimit.Plo @@ -5738,12 +5985,15 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-https-connect.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-ip-happy.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-recvbuf.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-cf-setup.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cf-socket.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-creds.Plo -rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo @@ -5815,13 +6065,14 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo -rm -f ./$(DEPDIR)/libcurlu_la-multi_ntfy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-peer.Plo -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo -rm -f ./$(DEPDIR)/libcurlu_la-protocol.Plo + -rm -f ./$(DEPDIR)/libcurlu_la-proxy.Plo -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo -rm -f ./$(DEPDIR)/libcurlu_la-ratelimit.Plo @@ -5920,12 +6171,20 @@ maintainer-clean: maintainer-clean-am -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-curl_ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-cmn.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2-proxy.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurl_la-cf-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_ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-curl_quiche.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-capsule.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-cmn.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2-proxy.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-ngtcp2.Plo + -rm -f vquic/$(DEPDIR)/libcurlu_la-cf-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 @@ -5945,6 +6204,7 @@ maintainer-clean: maintainer-clean-am -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_config.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo @@ -5960,6 +6220,7 @@ maintainer-clean: maintainer-clean-am -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo + -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_config.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo diff --git a/deps/curl/lib/Makefile.inc b/deps/curl/lib/Makefile.inc index d762f72e42..266ba52af3 100644 --- a/deps/curl/lib/Makefile.inc +++ b/deps/curl/lib/Makefile.inc @@ -96,6 +96,7 @@ LIB_VTLS_CFILES = \ vtls/schannel.c \ vtls/schannel_verify.c \ vtls/vtls.c \ + vtls/vtls_config.c \ vtls/vtls_scache.c \ vtls/vtls_spack.c \ vtls/wolfssl.c \ @@ -113,23 +114,32 @@ LIB_VTLS_HFILES = \ vtls/schannel.h \ vtls/schannel_int.h \ vtls/vtls.h \ + vtls/vtls_config.h \ vtls/vtls_int.h \ vtls/vtls_scache.h \ vtls/vtls_spack.h \ vtls/wolfssl.h \ vtls/x509asn1.h -LIB_VQUIC_CFILES = \ - vquic/curl_ngtcp2.c \ - vquic/curl_quiche.c \ - vquic/vquic.c \ +LIB_VQUIC_CFILES = \ + vquic/capsule.c \ + vquic/cf-capsule.c \ + vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c \ + vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c \ + vquic/vquic.c \ vquic/vquic-tls.c -LIB_VQUIC_HFILES = \ - vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ +LIB_VQUIC_HFILES = \ + vquic/capsule.h \ + vquic/cf-capsule.h \ + vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h \ + vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h LIB_VSSH_CFILES = \ @@ -155,12 +165,15 @@ LIB_CFILES = \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ + cf-recvbuf.c \ + cf-setup.c \ cf-socket.c \ cfilters.c \ conncache.c \ connect.c \ content_encoding.c \ cookie.c \ + creds.c \ cshutdn.c \ curl_addrinfo.c \ curl_endian.c \ @@ -230,13 +243,14 @@ LIB_CFILES = \ multi_ev.c \ multi_ntfy.c \ netrc.c \ - noproxy.c \ openldap.c \ parsedate.c \ + peer.c \ pingpong.c \ pop3.c \ progress.c \ protocol.c \ + proxy.c \ psl.c \ rand.c \ ratelimit.c \ @@ -285,6 +299,8 @@ LIB_HFILES = \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ + cf-recvbuf.h \ + cf-setup.h \ cf-socket.h \ cfilters.h \ conncache.h \ @@ -292,6 +308,7 @@ LIB_HFILES = \ connect.h \ content_encoding.h \ cookie.h \ + creds.h \ curl_addrinfo.h \ curl_ctype.h \ curl_endian.h \ @@ -362,12 +379,13 @@ LIB_HFILES = \ multi_ntfy.h \ multiif.h \ netrc.h \ - noproxy.h \ parsedate.h \ + peer.h \ pingpong.h \ pop3.h \ progress.h \ protocol.h \ + proxy.h \ psl.h \ rand.h \ ratelimit.h \ diff --git a/deps/curl/lib/altsvc.c b/deps/curl/lib/altsvc.c index 81b5379ad4..80913d9a1d 100644 --- a/deps/curl/lib/altsvc.c +++ b/deps/curl/lib/altsvc.c @@ -113,11 +113,11 @@ static struct altsvc *altsvc_createid(const char *srchost, return NULL; as->src.host = (char *)as + sizeof(struct altsvc); memcpy(as->src.host, srchost, hlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->dst.host = (char *)as + sizeof(struct altsvc) + hlen + 1; memcpy(as->dst.host, dsthost, dlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -149,7 +149,7 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost, static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as) { while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) { - /* It's full. Remove the first entry in the list */ + /* It is full. Remove the first entry in the list */ struct Curl_llist_node *e = Curl_llist_head(&asi->list); struct altsvc *oldas = Curl_node_elem(e); Curl_node_remove(e); diff --git a/deps/curl/lib/arpa_telnet.h b/deps/curl/lib/arpa_telnet.h index b5faab419c..826f937537 100644 --- a/deps/curl/lib/arpa_telnet.h +++ b/deps/curl/lib/arpa_telnet.h @@ -35,6 +35,7 @@ #define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */ #define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */ #define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */ + #define CURL_NEW_ENV_VAR 0 #define CURL_NEW_ENV_VALUE 1 diff --git a/deps/curl/lib/asyn-ares.c b/deps/curl/lib/asyn-ares.c index a4a7464285..d17a6038e2 100644 --- a/deps/curl/lib/asyn-ares.c +++ b/deps/curl/lib/asyn-ares.c @@ -281,8 +281,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(ares->ares_status == ARES_SUCCESS && !result) { struct Curl_dns_entry *dns = Curl_dnscache_mk_entry2(data, async->dns_queries, - &ares->res_AAAA, &ares->res_A, - async->hostname, async->port); + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -314,7 +314,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, } CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", - result, *pdns ? "" : "not "); + (int)result, *pdns ? "" : "not "); async_ares_cleanup(async); out: @@ -489,7 +489,7 @@ static struct Curl_addrinfo *async_ares_node2addr( struct Curl_addrinfo *calast = NULL; int error = 0; - for(ai = node; ai != NULL; ai = ai->ai_next) { + for(ai = node; ai; ai = ai->ai_next) { size_t ss_size; struct Curl_addrinfo *ca; /* ignore elements with unsupported address family, diff --git a/deps/curl/lib/asyn-thrdd.c b/deps/curl/lib/asyn-thrdd.c index 90f055c2a1..aeadec11a4 100644 --- a/deps/curl/lib/asyn-thrdd.c +++ b/deps/curl/lib/asyn-thrdd.c @@ -111,7 +111,7 @@ struct async_thrdd_item { #ifdef CURLVERBOSE char description[CURL_ASYN_ITEM_DESC_LEN]; #endif - int sock_error; + int sockerr; uint32_t mid; uint32_t resolv_id; uint16_t port; @@ -372,9 +372,9 @@ static void async_thrdd_item_process(void *arg) rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); if(rc) { - item->sock_error = SOCKERRNO ? SOCKERRNO : rc; - if(item->sock_error == 0) - item->sock_error = RESOLVER_ENOMEM; + item->sockerr = SOCKERRNO ? SOCKERRNO : rc; + if(item->sockerr == 0) + item->sockerr = RESOLVER_ENOMEM; } else { Curl_addrinfo_set_port(item->res, item->port); @@ -399,9 +399,9 @@ static void async_thrdd_item_process(void *arg) #endif item->res = Curl_ipv4_resolve_r(item->hostname, item->port); if(!item->res) { - item->sock_error = SOCKERRNO; - if(item->sock_error == 0) - item->sock_error = RESOLVER_ENOMEM; + item->sockerr = SOCKERRNO; + if(item->sockerr == 0) + item->sockerr = RESOLVER_ENOMEM; } } @@ -481,8 +481,13 @@ static void async_thrdd_report_item(struct Curl_easy *data, struct dynbuf tmp; const char *sep = ""; const struct Curl_addrinfo *ai = item->res; - int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; CURLcode result; + int ai_family; +#ifdef USE_IPV6 + ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; +#else + ai_family = AF_INET; +#endif if(!CURL_TRC_DNS_is_verbose(data)) return; @@ -635,7 +640,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, out: if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", - async->hostname, async->port, result); + async->hostname, async->port, (int)result); return result; } @@ -701,6 +706,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(thrdd->rr.channel) (void)Curl_ares_perform(thrdd->rr.channel, 0); #endif +#ifndef ENABLE_WAKEUP + Curl_async_thrdd_multi_process(data->multi); +#endif if(!async->done) return CURLE_AGAIN; @@ -756,7 +764,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, (result != CURLE_COULDNT_RESOLVE_HOST) && (result != CURLE_COULDNT_RESOLVE_PROXY)) { CURL_TRC_DNS(data, "Error %d resolving %s:%d", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } @@ -789,10 +797,12 @@ const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, if(thrdd->res_A) return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); break; +#ifdef USE_IPV6 case AF_INET6: if(thrdd->res_AAAA) return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); break; +#endif default: break; } diff --git a/deps/curl/lib/bufq.h b/deps/curl/lib/bufq.h index da411b586d..c53749d84e 100644 --- a/deps/curl/lib/bufq.h +++ b/deps/curl/lib/bufq.h @@ -204,6 +204,7 @@ void Curl_bufq_skip(struct bufq *q, size_t amount); typedef CURLcode Curl_bufq_writer(void *writer_ctx, const uint8_t *buf, size_t len, size_t *pwritten); + /** * Passes the chunks in the buffer queue to the writer and returns * the amount of buf written. A writer may return -1 and CURLE_AGAIN diff --git a/deps/curl/lib/cf-dns.c b/deps/curl/lib/cf-dns.c index e763b8ed38..631c987be2 100644 --- a/deps/curl/lib/cf-dns.c +++ b/deps/curl/lib/cf-dns.c @@ -37,48 +37,38 @@ struct cf_dns_ctx { struct Curl_dns_entry *dns; + struct Curl_peer *peer; CURLcode resolv_result; uint32_t resolv_id; - uint16_t port; uint8_t dns_queries; uint8_t transport; BIT(started); BIT(announced); - BIT(abstract_unix_socket); BIT(complete_resolve); BIT(for_proxy); - char hostname[1]; }; static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, + uint8_t transport, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { struct cf_dns_ctx *ctx; - size_t hlen = strlen(hostname); - ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) return NULL; - ctx->port = port; + Curl_peer_link(&ctx->peer, peer); ctx->dns_queries = dns_queries; ctx->transport = transport; - ctx->abstract_unix_socket = abstract_unix_socket; ctx->for_proxy = for_proxy; ctx->complete_resolve = complete_resolve; - ctx->dns = Curl_dns_entry_link(data, dns); - ctx->started = !!ctx->dns; - if(hlen) - memcpy(ctx->hostname, hostname, hlen); CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", - ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); + peer->hostname, peer->port, ctx->transport, ctx->dns_queries); return ctx; } @@ -86,6 +76,7 @@ static void cf_dns_ctx_destroy(struct Curl_easy *data, struct cf_dns_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->peer); Curl_dns_entry_unlink(data, &ctx->dns); curlx_free(ctx); } @@ -131,16 +122,14 @@ static void cf_dns_report(struct Curl_cfilter *cf, !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) return; - switch(ctx->transport) { - case TRNSPRT_UNIX: + if(ctx->peer->unix_socket) { #ifdef USE_UNIX_SOCKETS - CURL_TRC_CF(data, cf, "resolved unix domain %s", - Curl_conn_get_unix_path(data->conn)); + CURL_TRC_CF(data, cf, "resolved unix://%s", ctx->peer->hostname); #else DEBUGASSERT(0); #endif - break; - default: + } + else { curlx_dyn_init(&tmp, 1024); infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); #ifdef CURLRES_IPV6 @@ -161,7 +150,6 @@ static void cf_dns_report(struct Curl_cfilter *cf, } #endif curlx_dyn_free(&tmp); - break; } } #else @@ -181,24 +169,19 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, *pdns = NULL; -#ifdef USE_UNIX_SOCKETS - if(ctx->transport == TRNSPRT_UNIX) { - CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname); - return Curl_resolv_unix(data, ctx->hostname, - (bool)cf->conn->bits.abstract_unix_socket, pdns); - } -#endif - - /* Resolve target host right on */ - CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); - if(Curl_is_ipv4addr(ctx->hostname)) + CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", + ctx->peer->unix_socket ? "unix-domain-socket" : "host", + ctx->peer->hostname, ctx->peer->port); + if(ctx->peer->unix_socket) + ctx->dns_queries = 0; + else if(Curl_is_ipv4addr(ctx->peer->hostname)) ctx->dns_queries |= CURL_DNSQ_A; #ifdef USE_IPV6 - else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + else if(ctx->peer->ipv6) ctx->dns_queries |= CURL_DNSQ_AAAA; #endif - result = Curl_resolv(data, ctx->dns_queries, - ctx->hostname, ctx->port, ctx->transport, + + result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, (bool)ctx->for_proxy, timeout_ms, &ctx->resolv_id, pdns); DEBUGASSERT(!result || !*pdns); @@ -211,14 +194,14 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ failf(data, "Failed to resolve '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", ctx->hostname, + FMT_TIMEDIFF_T " ms", ctx->peer->hostname, curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } else { DEBUGASSERT(result); - failf(data, "Could not resolve: %s", ctx->hostname); + failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } } @@ -281,7 +264,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, } if(ctx->resolv_result) { - CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); return ctx->resolv_result; } @@ -327,13 +310,6 @@ static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf_dns_ctx_destroy(data, ctx); } -static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -379,7 +355,6 @@ struct Curl_cftype Curl_cft_dns = { CURL_LOG_LVL_NONE, cf_dns_destroy, cf_dns_connect, - cf_dns_close, Curl_cf_def_shutdown, cf_dns_adjust_pollset, Curl_cf_def_data_pending, @@ -393,23 +368,19 @@ struct Curl_cftype Curl_cft_dns = { static CURLcode cf_dns_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { struct Curl_cfilter *cf = NULL; struct cf_dns_ctx *ctx; CURLcode result = CURLE_OK; (void)data; - ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, - abstract_unix_socket, for_proxy, - complete_resolve, dns); + ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, + for_proxy, complete_resolve); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -424,88 +395,28 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, return result; } -/* Create a "resolv" filter for the transfer's connection. Figures - * out the hostname/path and port where to connect to. */ -static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - uint8_t dns_queries, - uint8_t transport, - bool complete_resolve, - struct Curl_dns_entry *dns) -{ - struct connectdata *conn = data->conn; - const char *hostname = NULL; - uint16_t port = 0; - bool abstract_unix_socket = FALSE, for_proxy = FALSE; - -#ifdef USE_UNIX_SOCKETS - { - const char *unix_path = Curl_conn_get_unix_path(conn); - if(unix_path) { - DEBUGASSERT(transport == TRNSPRT_UNIX); - hostname = unix_path; - abstract_unix_socket = (bool)conn->bits.abstract_unix_socket; - } - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(!hostname && conn->bits.proxy) { - for_proxy = TRUE; - hostname = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; - port = conn->bits.socksproxy ? - conn->socks_proxy.port : conn->http_proxy.port; - } -#endif - if(!hostname) { - struct hostname *ehost; - ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; - /* If not connecting via a proxy, extract the port from the URL, if it is - * there, thus overriding any defaults that might have been set above. */ - hostname = ehost->name; - port = conn->bits.conn_to_port ? - conn->conn_to_port : (uint16_t)conn->remote_port; - } - - if(!hostname) { - DEBUGASSERT(0); - return CURLE_FAILED_INIT; - } - return cf_dns_create(pcf, data, dns_queries, - hostname, port, transport, - abstract_unix_socket, for_proxy, - complete_resolve, dns); -} - /* Adds a "resolv" filter at the top of the connection's filter chain. - * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will - * figure out hostname and port to connect to and start the DNS resolve - * on the first connect attempt. - * For SECONDARYSOCKET, the `dns` parameter must be given. - */ + * The filter will resolve the peer on the first connect attempt. */ CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns) + uint8_t transport) { struct Curl_cfilter *cf = NULL; + bool for_proxy = FALSE; CURLcode result; - DEBUGASSERT(data); - if(sockindex == FIRSTSOCKET) - result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); - else if(dns) { - result = cf_dns_create(&cf, data, dns_queries, - dns->hostname, dns->port, transport, - FALSE, FALSE, FALSE, dns); - } - else { - DEBUGASSERT(0); - result = CURLE_FAILED_INIT; - } + if(!peer) + return CURLE_FAILED_INIT; +#ifndef CURL_DISABLE_PROXY + for_proxy = (peer == conn->socks_proxy.peer) || + (peer == conn->http_proxy.peer); +#endif + + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + for_proxy, FALSE); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -514,7 +425,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, } /* Insert a new "resolv" filter directly after `cf`. It will - * start a DNS resolve for the given hostnmae and port on the + * start a DNS resolve for the given peer on the * first connect attempt. * See socks.c on how this is used to make a non-blocking DNS * resolve during connect. @@ -522,17 +433,15 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve) { struct Curl_cfilter *cf; CURLcode result; - result = cf_dns_create(&cf, data, dns_queries, - hostname, port, transport, - FALSE, FALSE, complete_resolve, NULL); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + FALSE, complete_resolve); if(result) return result; @@ -543,29 +452,35 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, /* Return the resolv result from the first "resolv" filter, starting * the given filter `cf` downwards. */ -static CURLcode cf_dns_result(struct Curl_cfilter *cf) +static CURLcode cf_dns_result(struct Curl_cfilter *cf, + struct Curl_peer *peer) { for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(ctx->dns || ctx->resolv_result) - return ctx->resolv_result; - return CURLE_AGAIN; + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + return CURLE_OK; /* ok, but no results */ } } return CURLE_FAILED_INIT; } -/* Return the result of the DNS resolution. Searches for a "resolv" +/* Return the result of the DNS resolution for peer. Searches for a "resolv" * filter from the top of the filter chain down. Returns * - CURLE_AGAIN when not done yet * - CURLE_OK when DNS was successfully resolved * - CURLR_FAILED_INIT when no resolv filter was found * - error returned by the DNS resolv */ -CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex) +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - return cf_dns_result(conn->cfilter[sockindex]); + return cf_dns_result(conn->cfilter[sockindex], peer); } static const struct Curl_addrinfo *cf_dns_get_nth_ai( @@ -598,6 +513,7 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( */ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_peer *peer, int ai_family, unsigned int index) { @@ -605,12 +521,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(ctx->resolv_result) - return NULL; - else if(ctx->dns) - return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); - else - return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->resolv_result) + return NULL; + else if(ctx->dns) + return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else + return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + } } } return NULL; @@ -621,11 +539,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, * not done yet or if no address for the family exists, returns NULL. */ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, - int sockindex, int ai_family, + struct Curl_peer *peer, + int sockindex, + int ai_family, unsigned int index) { struct connectdata *conn = data->conn; - return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index); + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer, + ai_family, index); } #ifdef USE_HTTPSRR @@ -633,35 +554,43 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, * connection. If the DNS resolving is not done yet or if there * is no HTTPS-RR info, returns NULL. */ -const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, - int sockindex) +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(ctx->dns) - return ctx->dns->hinfo; - else - return Curl_resolv_get_https(data, ctx->resolv_id); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns) + return ctx->dns->hinfo; + else + return Curl_resolv_get_https(data, ctx->resolv_id); + } } } return NULL; } -bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(ctx->dns) - return TRUE; - else - return Curl_resolv_knows_https(data, ctx->resolv_id); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } } } - return FALSE; + return TRUE; } #endif /* USE_HTTPSRR */ diff --git a/deps/curl/lib/cf-dns.h b/deps/curl/lib/cf-dns.h index 3c46b1bf3d..891b1efea6 100644 --- a/deps/curl/lib/cf-dns.h +++ b/deps/curl/lib/cf-dns.h @@ -29,41 +29,49 @@ struct Curl_easy; struct connectdata; struct Curl_dns_entry; struct Curl_addrinfo; +struct Curl_peer; CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns); + uint8_t transport); CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, uint8_t dns_queries, - const char *hostname, - uint16_t port, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve); -CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + struct Curl_peer *peer, int sockindex, int ai_family, unsigned int index); const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_peer *peer, int ai_family, unsigned int index); #ifdef USE_HTTPSRR -const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, - int sockindex); -bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); #else -#define Curl_conn_dns_get_https(a, b) NULL -#define Curl_conn_dns_resolved_https(a, b) TRUE +#define Curl_conn_dns_get_https(a, b, c) NULL +#define Curl_conn_dns_resolved_https(a, b, c) TRUE #endif extern struct Curl_cftype Curl_cft_dns; diff --git a/deps/curl/lib/cf-h1-proxy.c b/deps/curl/lib/cf-h1-proxy.c index 3c2c8374d1..f1e5c7a42a 100644 --- a/deps/curl/lib/cf-h1-proxy.c +++ b/deps/curl/lib/cf-h1-proxy.c @@ -25,6 +25,8 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include #include "urldata.h" #include "curlx/dynbuf.h" #include "sendf.h" @@ -33,6 +35,7 @@ #include "http_proxy.h" #include "select.h" #include "progress.h" +#include "multiif.h" #include "cfilters.h" #include "cf-h1-proxy.h" #include "connect.h" @@ -40,7 +43,6 @@ #include "strcase.h" #include "curlx/strparse.h" - typedef enum { H1_TUNNEL_INIT, /* init/default/no tunnel state */ H1_TUNNEL_CONNECT, /* CONNECT request is being send */ @@ -52,11 +54,13 @@ typedef enum { /* struct for HTTP CONNECT tunneling */ struct h1_tunnel_state { + struct Curl_peer *dest; struct dynbuf rcvbuf; struct dynbuf request_data; size_t nsent; size_t headerlines; struct Curl_chunker ch; + int httpversion; enum keeponval { KEEPON_DONE, KEEPON_CONNECT, @@ -70,6 +74,12 @@ struct h1_tunnel_state { BIT(leading_unfold); }; +/* Persistent context for the H1-PROXY filter */ +struct cf_h1_proxy_ctx { + struct h1_tunnel_state *ts; + BIT(udp_tunnel); +}; + static bool tunnel_is_established(struct h1_tunnel_state *ts) { return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); @@ -80,6 +90,12 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts) return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); } +static bool h1_proxy_is_udp(struct Curl_cfilter *cf) +{ + struct cf_h1_proxy_ctx *pctx = cf->ctx; + return (pctx->udp_tunnel ? TRUE : FALSE); +} + static CURLcode tunnel_reinit(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) @@ -95,6 +111,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf, ts->close_connection = FALSE; ts->maybe_folded = FALSE; ts->leading_unfold = FALSE; + ts->nsent = 0; + ts->headerlines = 0; return CURLE_OK; } @@ -156,7 +174,9 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, case H1_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "new tunnel state 'established'"); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP proxy", + h1_proxy_is_udp(cf) ? "-UDP" : ""); + data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -172,23 +192,33 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request after we have connected. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } -static void tunnel_free(struct Curl_cfilter *cf, +static void tunnel_free(struct h1_tunnel_state *ts, struct Curl_easy *data) +{ + if(ts) { + Curl_peer_unlink(&ts->dest); + curlx_dyn_free(&ts->rcvbuf); + curlx_dyn_free(&ts->request_data); + Curl_httpchunk_free(data, &ts->ch); + curlx_free(ts); + } +} + +static void cf_tunnel_free(struct Curl_cfilter *cf, + struct Curl_easy *data) { if(cf) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; if(ts) { h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - curlx_dyn_free(&ts->rcvbuf); - curlx_dyn_free(&ts->request_data); - Curl_httpchunk_free(data, &ts->ch); - curlx_free(ts); - cf->ctx = NULL; + tunnel_free(ts, data); + pctx->ts = NULL; } } } @@ -206,20 +236,21 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, int http_minor; CURLcode result; + DEBUGASSERT(data); /* This only happens if we have looped here due to authentication reasons, and we do not really use the newly cloned URL here then. Free it. */ curlx_safefree(data->req.newurl); - result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ts->dest, + PROXY_HTTP_V1, + h1_proxy_is_udp(cf)); if(result) goto out; - infof(data, "Establish HTTP proxy tunnel to %s", req->authority); - curlx_dyn_reset(&ts->request_data); ts->nsent = 0; ts->headerlines = 0; - http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; + http_minor = ts->httpversion % 10; result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); if(!result) @@ -268,6 +299,92 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, return result; } +static CURLcode on_resp_header_udp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + const char *header) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + + if((checkprefix("WWW-Authenticate:", header) && (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", header) && (407 == k->httpcode))) { + + bool proxy = (k->httpcode == 407); + char *auth = Curl_copy_header_value(header); + if(!auth) + return CURLE_OUT_OF_MEMORY; + + CURL_TRC_CF(data, cf, "CONNECT-UDP: fwd auth header '%s'", header); + result = Curl_http_input_auth(data, proxy, auth); + + curlx_free(auth); + + if(result) + return result; + } + else if(checkprefix("Content-Length:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Content-Length in CONNECT-UDP %03d response", + k->httpcode); + } + else { + const char *p = header + strlen("Content-Length:"); + if(curlx_str_numblanks(&p, &ts->cl)) { + failf(data, "Unsupported Content-Length value"); + return CURLE_WEIRD_SERVER_REPLY; + } + } + } + else if(checkprefix("Transfer-Encoding:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Transfer-Encoding in " + "CONNECT-UDP %03d response", k->httpcode); + } + else if(Curl_compareheader(header, + STRCONST("Transfer-Encoding:"), + STRCONST("chunked"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> " + "Transfer-Encoding: chunked"); + ts->chunked_encoding = TRUE; + /* reset our chunky engine */ + Curl_httpchunk_reset(data, &ts->ch, TRUE); + } + } + else if(checkprefix("Capsule-protocol:", header)) { + if(Curl_compareheader(header, + STRCONST("Capsule-protocol:"), + STRCONST("?1"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Capsule-protocol: ?1"); + } + } + else if(Curl_compareheader(header, + STRCONST("Connection:"), STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Connection: close"); + } + else if(Curl_compareheader(header, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, + "CONNECT-UDP Response --> Proxy-Connection: close"); + } + else if(!strncmp(header, "HTTP/1.", 7) && + ((header[7] == '0') || (header[7] == '1')) && + (header[8] == ' ') && + ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && + !ISDIGIT(header[12])) { + /* store the HTTP code from the proxy */ + data->info.httpproxycode = k->httpcode = + ((header[9] - '0') * 100) + + ((header[10] - '0') * 10) + + (header[11] - '0'); + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> %d", k->httpcode); + } + return result; +} + static CURLcode on_resp_header(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, @@ -359,6 +476,12 @@ static CURLcode single_header(struct Curl_cfilter *cf, /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); + /* a CONNECT response line is handed to the client as a header, so it must + pass the same checks as a regular response header before delivery */ + result = Curl_verify_header(data, linep, line_len); + if(result) + return result; + /* send the header to the callback */ writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); @@ -406,7 +529,13 @@ static CURLcode single_header(struct Curl_cfilter *cf, return result; } - result = on_resp_header(cf, data, ts, linep); + if(h1_proxy_is_udp(cf)) { + result = on_resp_header_udp(cf, data, ts, linep); + } + else { + result = on_resp_header(cf, data, ts, linep); + } + if(result) return result; @@ -448,8 +577,15 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, } if(!nread) { + if(ts->maybe_folded) { + /* EOF right after LF: finalize the pending header line. */ + result = single_header(cf, data, ts); + if(result) + return result; + ts->maybe_folded = FALSE; + } if(data->set.proxyauth && data->state.authproxy.avail && - data->req.proxyuserpwd) { + data->req.hd_proxy_auth) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -539,12 +675,16 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, ts->maybe_folded = TRUE; } + if(result) + return result; } /* while there is buffer left and loop is requested */ if(error) result = CURLE_RECV_ERROR; *done = (ts->keepon == KEEPON_DONE); - if(!result && *done && data->info.httpproxycode / 100 != 2) { + if(!result && *done && + data->info.httpproxycode / 100 != 2 && + !(h1_proxy_is_udp(cf) && data->info.httpproxycode == 101)) { /* Deal with the possibly already received authenticate headers. 'newurl' is set to a new URL if we must loop. */ result = Curl_http_auth_act(data); @@ -597,7 +737,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); if(result) - CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", + (int)result); if(!result) result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ @@ -623,9 +764,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, */ CURL_TRC_CF(data, cf, "CONNECT need to close+open"); infof(data, "Connect me again please"); - Curl_conn_cf_close(cf, data); - result = Curl_conn_cf_connect(cf->next, data, &done); - goto out; + return CURLE_AGAIN; } else { /* staying on this connection, reset state */ @@ -641,17 +780,36 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, } while(data->req.newurl); DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); - if(data->info.httpproxycode / 100 != 2) { - /* a non-2xx response and we have no next URL to try. */ - curlx_safefree(data->req.newurl); - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_COULDNT_CONNECT; + if(h1_proxy_is_udp(cf)) { + /* RFC 9298: Accept 101 Upgrade for HTTP/1.1 and + * 2xx responses for HTTP/2 and HTTP/3 proxies. */ + if(data->info.httpproxycode / 100 != 2 && + data->info.httpproxycode != 101) { + curlx_safefree(data->req.newurl); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT-UDP tunnel failed, response %d", + data->req.httpcode); + return CURLE_COULDNT_CONNECT; + } + } + else { + if(data->info.httpproxycode / 100 != 2) { + /* a non-2xx response and we have no next URL to try. */ + curlx_safefree(data->req.newurl); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); + return CURLE_COULDNT_CONNECT; + } } /* 2xx response, SUCCESS! */ + /* 101 Switching Protocol for CONNECT-UDP */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); - infof(data, "CONNECT tunnel established, response %d", - data->info.httpproxycode); + if(h1_proxy_is_udp(cf)) + infof(data, "CONNECT-UDP tunnel established, response %d", + data->info.httpproxycode); + else + infof(data, "CONNECT tunnel established, response %d", + data->info.httpproxycode); result = CURLE_OK; out: @@ -665,7 +823,8 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, bool *done) { CURLcode result; - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; if(cf->connected) { *done = TRUE; @@ -682,7 +841,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = tunnel_init(cf, data, &ts); if(result) return result; - cf->ctx = ts; + pctx->ts = ts; } /* We want "seamless" operations through HTTP proxy tunnel */ @@ -690,18 +849,17 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = H1_CONNECT(cf, data, ts); if(result) goto out; - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); out: - *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); + *done = (result == CURLE_OK) && tunnel_is_established(pctx->ts); if(*done) { cf->connected = TRUE; /* The real request will follow the CONNECT, reset request partially */ Curl_req_soft_reset(&data->req, data); Curl_client_reset(data); Curl_pgrsReset(data); - - tunnel_free(cf, data); + cf_tunnel_free(cf, data); } return result; } @@ -710,7 +868,8 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; CURLcode result = CURLE_OK; if(!cf->connected) { @@ -730,28 +889,52 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, else result = Curl_pollset_set_out_only(data, ps, sock); } + else { + if(cf->next) + result = cf->next->cft->adjust_pollset(cf->next, data, ps); + } return result; } +static bool cf_h1_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "destroy"); - tunnel_free(cf, data); + cf_tunnel_free(cf, data); + curlx_safefree(cf->ctx); } -static void cf_h1_proxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { - CURL_TRC_CF(data, cf, "close"); - if(cf) { - cf->connected = FALSE; - if(cf->ctx) { - h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; + switch(query) { + case CF_QUERY_HOST_PORT: + if(!ts || !ts->dest) + break; + *pres1 = (int)ts->dest->port; + *((const char **)pres2) = ts->dest->hostname; + return CURLE_OK; + case CF_QUERY_ALPN_NEGOTIATED: { + const char **palpn = pres2; + DEBUGASSERT(palpn); + *palpn = NULL; + return CURLE_OK; + } + default: + break; } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; } struct Curl_cftype Curl_cft_h1_proxy = { @@ -760,28 +943,62 @@ struct Curl_cftype Curl_cft_h1_proxy = { 0, cf_h1_proxy_destroy, cf_h1_proxy_connect, - cf_h1_proxy_close, Curl_cf_def_shutdown, cf_h1_proxy_adjust_pollset, - Curl_cf_def_data_pending, + cf_h1_proxy_data_pending, Curl_cf_def_send, Curl_cf_def_recv, Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_h1_proxy_query, }; CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion, + bool udp_tunnel) { struct Curl_cfilter *cf; + struct cf_h1_proxy_ctx *pctx; + struct h1_tunnel_state *ts; CURLcode result; (void)data; - result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); + if(!dest) + return CURLE_FAILED_INIT; + if((httpversion < 10) || (httpversion >= 20)) + return CURLE_FAILED_INIT; + + ts = curlx_calloc(1, sizeof(*ts)); + if(!ts) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_peer_link(&ts->dest, dest); + ts->httpversion = httpversion; + curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); + curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); + Curl_httpchunk_init(data, &ts->ch, TRUE); + + pctx = curlx_calloc(1, sizeof(*pctx)); + if(!pctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pctx->udp_tunnel = udp_tunnel; + pctx->ts = ts; + result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, pctx); + if(result) { + curlx_free(pctx); + goto out; + } + ts = NULL; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + tunnel_free(ts, data); return result; } diff --git a/deps/curl/lib/cf-h1-proxy.h b/deps/curl/lib/cf-h1-proxy.h index 6544ec58d0..3255bf79f2 100644 --- a/deps/curl/lib/cf-h1-proxy.h +++ b/deps/curl/lib/cf-h1-proxy.h @@ -27,8 +27,13 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +struct Curl_peer; + CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h1_proxy; diff --git a/deps/curl/lib/cf-h2-proxy.c b/deps/curl/lib/cf-h2-proxy.c index 2f8cc41dd5..be303ffd3e 100644 --- a/deps/curl/lib/cf-h2-proxy.c +++ b/deps/curl/lib/cf-h2-proxy.c @@ -76,30 +76,40 @@ struct tunnel_stream { BIT(reset); }; -static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, - struct tunnel_stream *ts) +static CURLcode tunnel_stream_init(struct tunnel_stream *ts, + struct Curl_peer *dest) { - const char *hostname; - uint16_t port; - bool ipv6_ip; - ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } +static void tunnel_stream_reset(struct tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + Curl_bufq_reset(&ts->recvbuf); + Curl_bufq_reset(&ts->sendbuf); + ts->stream_id = -1; + ts->error = 0; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->reset = FALSE; + ts->state = H2_TUNNEL_INIT; +} + static void tunnel_stream_clear(struct tunnel_stream *ts) { Curl_http_resp_free(ts->resp); @@ -113,9 +123,11 @@ static void tunnel_stream_clear(struct tunnel_stream *ts) static void h2_tunnel_go_state(struct Curl_cfilter *cf, struct tunnel_stream *ts, h2_tunnel_state new_state, - struct Curl_easy *data) + struct Curl_easy *data, + bool udp_tunnel) { (void)cf; + (void)udp_tunnel; if(ts->state == new_state) return; @@ -131,7 +143,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, switch(new_state) { case H2_TUNNEL_INIT: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); - tunnel_stream_clear(ts); + tunnel_stream_reset(ts); break; case H2_TUNNEL_CONNECT: @@ -147,7 +159,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, case H2_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", ts->stream_id); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP/2 proxy", + udp_tunnel ? "-UDP" : ""); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -158,7 +171,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request after we have connected. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } @@ -171,6 +184,7 @@ struct cf_h2_proxy_ctx { struct bufq inbufq; /* network receive buffer */ struct bufq outbufq; /* network send buffer */ + struct Curl_peer *dest; /* where to tunnel to */ struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ int32_t goaway_error; int32_t last_stream_id; @@ -178,6 +192,7 @@ struct cf_h2_proxy_ctx { BIT(rcvd_goaway); BIT(sent_goaway); BIT(nw_out_blocked); + BIT(udp_tunnel); }; /* How to access `call_data` from a cf_h2 filter */ @@ -193,6 +208,7 @@ static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) } Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); + Curl_peer_unlink(&ctx->dest); tunnel_stream_clear(&ctx->tunnel); memset(ctx, 0, sizeof(*ctx)); ctx->call_data = save; @@ -213,7 +229,8 @@ static void drain_tunnel(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; (void)cf; if(!tunnel->closed && !tunnel->reset && - !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) + (!Curl_bufq_is_empty(&ctx->tunnel.sendbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) Curl_multi_mark_dirty(data); } @@ -228,7 +245,7 @@ static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, CURLcode result; result = Curl_conn_cf_send(cf->next, data, buf, buflen, FALSE, pnwritten); CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", - buflen, result, *pnwritten); + buflen, (int)result, *pnwritten); return result; } return CURLE_FAILED_INIT; @@ -247,8 +264,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); @@ -354,7 +370,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu", - Curl_bufq_len(&ctx->inbufq), result, nread); + Curl_bufq_len(&ctx->inbufq), (int)result, nread); if(result) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 proxy data"); @@ -363,6 +379,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, break; } else if(nread == 0) { + CURL_TRC_CF(data, cf, "server closed connection"); ctx->conn_closed = TRUE; break; } @@ -558,12 +575,12 @@ static int proxy_h2_on_header(nghttp2_session *session, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { int http_status; struct http_resp *resp; /* status: always comes first, we might get more than one response, - * link the previous ones for keepers */ + * discard previous, interim responses */ result = Curl_http_decode_status(&http_status, (const char *)value, valuelen); if(result) @@ -571,7 +588,8 @@ static int proxy_h2_on_header(nghttp2_session *session, result = Curl_http_resp_make(&resp, http_status, NULL); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - resp->prev = ctx->tunnel.resp; + if(ctx->tunnel.resp) + Curl_http_resp_free(ctx->tunnel.resp); ctx->tunnel.resp = resp; CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", stream_id, ctx->tunnel.resp->status); @@ -750,15 +768,15 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, CURLcode result; struct httpreq *req = NULL; - result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ctx->dest, + PROXY_HTTP_V2, + (bool)ctx->udp_tunnel); if(result) goto out; result = Curl_creader_set_null(data); if(result) goto out; - infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); - result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, NULL, ts, tunnel_send_callback, cf); if(result) { @@ -778,41 +796,30 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, struct Curl_easy *data, struct tunnel_stream *ts) { - CURLcode result = CURLE_OK; - struct dynhds_entry *auth_reply = NULL; - (void)cf; - - DEBUGASSERT(ts->resp); - if(ts->resp->status / 100 == 2) { - infof(data, "CONNECT tunnel established, response %d", ts->resp->status); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); - return CURLE_OK; - } - - if(ts->resp->status == 401) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); - } - else if(ts->resp->status == 407) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); - } + struct cf_h2_proxy_ctx *ctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; - if(auth_reply) { - CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", - auth_reply->value); - result = Curl_http_input_auth(data, ts->resp->status == 407, - auth_reply->value); - if(result) - return result; - if(data->req.newurl) { - /* Indicator that we should try again */ - curlx_safefree(data->req.newurl); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); - return CURLE_OK; - } + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)ctx->udp_tunnel, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data, + (bool)ctx->udp_tunnel); + break; + case PROXY_INSPECT_FAILED: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data, + (bool)ctx->udp_tunnel); + break; } - - /* Seems to have failed */ - return CURLE_COULDNT_CONNECT; + return result; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -824,6 +831,11 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(ts); DEBUGASSERT(ts->authority); + if(ctx->conn_closed) { + failf(data, "proxy closed connection"); + return CURLE_COULDNT_CONNECT; + } + do { switch(ts->state) { case H2_TUNNEL_INIT: @@ -832,7 +844,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, result = submit_CONNECT(cf, data, ts); if(result) goto out; - h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data, + (bool)ctx->udp_tunnel); FALLTHROUGH(); case H2_TUNNEL_CONNECT: @@ -841,12 +854,14 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, if(!result) result = proxy_h2_progress_egress(cf, data); if(result && result != CURLE_AGAIN) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); break; } if(ts->has_final_response) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data, + (bool)ctx->udp_tunnel); } else { result = CURLE_OK; @@ -875,7 +890,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, out: if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); return result; } @@ -896,7 +912,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); - if(tunnel_stream_init(cf, &ctx->tunnel)) + if(tunnel_stream_init(&ctx->tunnel, ctx->dest)) goto out; rc = nghttp2_session_callbacks_new(&cbs); @@ -958,7 +974,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, out: if(cbs) nghttp2_session_callbacks_del(cbs); - CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", (int)result); return result; } @@ -1016,21 +1032,6 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, return result; } -static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_proxy_ctx_clear(ctx); - CF_DATA_RESTORE(cf, save); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1133,7 +1134,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } else if(ctx->sent_goaway && !cf->shutdown) { @@ -1145,7 +1146,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, want_recv = nghttp2_session_want_read(ctx->h2); result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } return result; @@ -1194,7 +1195,7 @@ static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); return result; } @@ -1232,14 +1233,15 @@ static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ drain_tunnel(cf, data, &ctx->tunnel); } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); CF_DATA_RESTORE(cf, save); return result; } @@ -1269,7 +1271,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", (int)result, + *pnwritten); if(result && (result != CURLE_AGAIN)) goto out; @@ -1298,7 +1301,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ @@ -1306,7 +1310,7 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, len, result, *pnwritten, + ctx->tunnel.stream_id, len, (int)result, *pnwritten, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -1338,7 +1342,7 @@ static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, result, + ctx->tunnel.stream_id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -1410,8 +1414,8 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->dest->port; + *((const char **)pres2) = ctx->dest->hostname; return CURLE_OK; case CF_QUERY_NEED_FLUSH: { if(!Curl_bufq_is_empty(&ctx->outbufq) || @@ -1464,7 +1468,6 @@ struct Curl_cftype Curl_cft_h2_proxy = { CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, - cf_h2_proxy_close, cf_h2_proxy_shutdown, cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, @@ -1477,7 +1480,9 @@ struct Curl_cftype Curl_cft_h2_proxy = { }; CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel) { struct Curl_cfilter *cf_h2_proxy = NULL; struct cf_h2_proxy_ctx *ctx; @@ -1487,17 +1492,17 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; + Curl_peer_link(&ctx->dest, dest); + ctx->udp_tunnel = udp_tunnel; result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); if(result) goto out; - + ctx = NULL; Curl_conn_cf_insert_after(cf, cf_h2_proxy); - result = CURLE_OK; out: - if(result) - cf_h2_proxy_ctx_free(ctx); + cf_h2_proxy_ctx_free(ctx); return result; } diff --git a/deps/curl/lib/cf-h2-proxy.h b/deps/curl/lib/cf-h2-proxy.h index 318ce1973f..07e3c9aedf 100644 --- a/deps/curl/lib/cf-h2-proxy.h +++ b/deps/curl/lib/cf-h2-proxy.h @@ -28,7 +28,9 @@ #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h2_proxy; diff --git a/deps/curl/lib/cf-haproxy.c b/deps/curl/lib/cf-haproxy.c index 9ee5e790eb..16ee25066c 100644 --- a/deps/curl/lib/cf-haproxy.c +++ b/deps/curl/lib/cf-haproxy.c @@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "connect.h" #include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -44,13 +45,6 @@ struct cf_haproxy_ctx { struct dynbuf data_out; }; -static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) -{ - DEBUGASSERT(ctx); - ctx->state = HAPROXY_INIT; - curlx_dyn_reset(&ctx->data_out); -} - static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) { if(ctx) { @@ -78,7 +72,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, DEBUGASSERT(ctx); DEBUGASSERT(ctx->state == HAPROXY_INIT); #ifdef USE_UNIX_SOCKETS - if(cf->conn->unix_domain_socket) + if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket) /* the buffer is large enough to hold this! */ result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); else { @@ -97,7 +91,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, client_dest_ip = ipquad.remote_ip; } - result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", + result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %d %d\r\n", is_ipv6 ? "TCP6" : "TCP4", client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); @@ -172,16 +166,6 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, cf_haproxy_ctx_free(cf->ctx); } -static void cf_haproxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - cf_haproxy_ctx_reset(cf->ctx); - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -201,7 +185,6 @@ struct Curl_cftype Curl_cft_haproxy = { 0, cf_haproxy_destroy, cf_haproxy_connect, - cf_haproxy_close, Curl_cf_def_shutdown, cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, diff --git a/deps/curl/lib/cf-https-connect.c b/deps/curl/lib/cf-https-connect.c index d1d8f51076..1a2e966ef6 100644 --- a/deps/curl/lib/cf-https-connect.c +++ b/deps/curl/lib/cf-https-connect.c @@ -29,6 +29,7 @@ #include "curl_trc.h" #include "cfilters.h" #include "cf-dns.h" +#include "cf-setup.h" #include "connect.h" #include "hostip.h" #include "httpsrr.h" @@ -62,7 +63,6 @@ static void cf_hc_baller_discard(struct cf_hc_baller *b, struct Curl_easy *data) { if(b->cf) { - Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } @@ -304,13 +304,14 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, size_t i; /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https(data, cf->sockindex); + rr = Curl_conn_dns_get_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); /* We do not support `rr->no_def_alpn`. */ if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; - if(alpn_rr == not_this_one) /* don't want this one */ + if(alpn_rr == not_this_one) /* do not want this one */ continue; switch(alpn_rr) { case ALPN_h3: @@ -493,7 +494,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->httpsrr_resolved) { - ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); #ifdef DEBUGBUILD if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); @@ -576,7 +578,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); return result; } @@ -616,7 +618,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, result = ctx->ballers[i].result; } } - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -635,7 +637,8 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } @@ -731,18 +734,6 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, return result; } -static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf_hc_ctx_close(data, cf->ctx); - cf->connected = FALSE; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; @@ -757,7 +748,6 @@ struct Curl_cftype Curl_cft_http_connect = { CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, - cf_hc_close, cf_hc_shutdown, cf_hc_adjust_pollset, cf_hc_data_pending, @@ -822,9 +812,14 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS); + /* This filter is intended for HTTPS using ALPN and does + * not support HTTPS Eyeballing to a proxy. */ if((conn->scheme->protocol != CURLPROTO_HTTPS) || +#ifndef CURL_DISABLE_PROXY + conn->bits.origin_is_proxy || +#endif !conn->bits.tls_enable_alpn) - goto out; + goto out; result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); diff --git a/deps/curl/lib/cf-ip-happy.c b/deps/curl/lib/cf-ip-happy.c index f67273e489..963ccea94d 100644 --- a/deps/curl/lib/cf-ip-happy.c +++ b/deps/curl/lib/cf-ip-happy.c @@ -56,12 +56,14 @@ #include "multiif.h" #include "progress.h" #include "select.h" +#include "sockaddr.h" #include "vquic/vquic.h" /* for quic cfilters */ struct transport_provider { - uint8_t transport; cf_ip_connect_create *cf_create; + uint8_t transport; + bool tunnel; }; static @@ -69,23 +71,30 @@ static const #endif struct transport_provider transport_providers[] = { - { TRNSPRT_TCP, Curl_cf_tcp_create }, + { Curl_cf_tcp_create, TRNSPRT_TCP, FALSE }, + { Curl_cf_tcp_create, TRNSPRT_TCP, TRUE }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) - { TRNSPRT_QUIC, Curl_cf_quic_create }, + { Curl_cf_quic_create, TRNSPRT_QUIC, FALSE }, +#endif +#if !defined(CURL_DISABLE_HTTP) && defined(USE_PROXY_HTTP3) + { Curl_cf_h3_proxy_create, TRNSPRT_QUIC, TRUE }, #endif #ifndef CURL_DISABLE_TFTP - { TRNSPRT_UDP, Curl_cf_udp_create }, + { Curl_cf_udp_create, TRNSPRT_UDP, FALSE }, #endif #ifdef USE_UNIX_SOCKETS - { TRNSPRT_UNIX, Curl_cf_unix_create }, + { Curl_cf_unix_create, TRNSPRT_UNIX, FALSE }, + { Curl_cf_unix_create, TRNSPRT_UNIX, TRUE }, #endif }; -static cf_ip_connect_create *get_cf_create(uint8_t transport) +static cf_ip_connect_create *get_cf_create(uint8_t transport, + bool tunnel) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { - if(transport == transport_providers[i].transport) + if((transport == transport_providers[i].transport) && + (tunnel == transport_providers[i].tunnel)) return transport_providers[i].cf_create; } return NULL; @@ -102,7 +111,6 @@ UNITTEST void debug_set_transport_provider( for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { if(transport == transport_providers[i].transport) { transport_providers[i].cf_create = cf_create; - return; } } } @@ -110,15 +118,18 @@ UNITTEST void debug_set_transport_provider( struct cf_ai_iter { struct Curl_cfilter *cf; + struct Curl_peer *peer; int ai_family; unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, struct Curl_cfilter *cf, + struct Curl_peer *peer, int ai_family) { iter->cf = cf; + iter->peer = peer; /* not linked, ctx->ballers owns and has same lifetime */ iter->ai_family = ai_family; iter->n = 0; } @@ -131,7 +142,7 @@ static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, if(!iter->cf) return NULL; - addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + addr = Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n); if(addr) iter->n++; @@ -142,19 +153,23 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, struct Curl_easy *data) { return (iter->cf && - !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + !!Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n)); } struct cf_ip_attempt { struct cf_ip_attempt *next; + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; struct Curl_sockaddr_ex addr; struct Curl_cfilter *cf; /* current sub-cfilter connecting */ cf_ip_connect_create *cf_create; struct curltime started; /* start of current attempt */ CURLcode result; int ai_family; - uint8_t transport; + uint8_t transport_peer; + uint8_t tunnel_transport; int error; BIT(connected); /* cf has connected */ BIT(shutdown); /* cf has shutdown */ @@ -168,16 +183,23 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a, if(a) { if(a->cf) Curl_conn_cf_discard_chain(&a->cf, data); + Curl_peer_unlink(&a->origin); + Curl_peer_unlink(&a->peer); + Curl_peer_unlink(&a->tunnel_peer); curlx_free(a); } } static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, - struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct Curl_sockaddr_ex *addr, int ai_family, - uint8_t transport, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, cf_ip_connect_create *cf_create) { struct Curl_cfilter *wcf; @@ -189,14 +211,20 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, if(!a) return CURLE_OUT_OF_MEMORY; + Curl_peer_link(&a->origin, origin); + Curl_peer_link(&a->peer, peer); + a->transport_peer = transport_peer; + Curl_peer_link(&a->tunnel_peer, tunnel_peer); + a->tunnel_transport = tunnel_transport; a->addr = *addr; a->ai_family = ai_family; - a->transport = transport; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); + result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, a->tunnel_peer, + a->tunnel_transport); if(result) goto out; @@ -245,13 +273,17 @@ struct cf_ip_ballers { #ifdef USE_IPV6 struct cf_ai_iter ipv6_iter; #endif + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; cf_ip_connect_create *cf_create; /* for creating cf */ struct curltime started; struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; uint32_t max_concurrent; - uint8_t transport; + uint8_t transport_peer; + uint8_t tunnel_transport; }; static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, @@ -269,7 +301,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, a->inconclusive = FALSE; a->cf = NULL; - result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); + result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, + a->tunnel_peer, a->tunnel_transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -282,11 +316,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, return result; } -static void cf_ip_ballers_clear(struct Curl_cfilter *cf, - struct Curl_easy *data, +static void cf_ip_ballers_clear(struct Curl_easy *data, struct cf_ip_ballers *bs) { - (void)cf; while(bs->running) { struct cf_ip_attempt *a = bs->running; bs->running = a->next; @@ -294,35 +326,36 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf, } cf_ip_attempt_free(bs->winner, data); bs->winner = NULL; + Curl_peer_unlink(&bs->origin); + Curl_peer_unlink(&bs->peer); + Curl_peer_unlink(&bs->tunnel_peer); } static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, - struct Curl_cfilter *cf, - cf_ip_connect_create *cf_create, - uint8_t transport, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, timediff_t attempt_delay_ms, uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); - bs->cf_create = cf_create; - bs->transport = transport; + bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer); + if(!bs->cf_create) { + failf(data, "unsupported transport type %u%s", + transport_peer, tunnel_peer ? " to proxy" : ""); + return CURLE_UNSUPPORTED_PROTOCOL; + } + Curl_peer_link(&bs->origin, origin); + Curl_peer_link(&bs->peer, peer); + bs->transport_peer = transport_peer; + Curl_peer_link(&bs->tunnel_peer, tunnel_peer); + bs->tunnel_transport = tunnel_transport; bs->attempt_delay_ms = attempt_delay_ms; bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ - - if(transport == TRNSPRT_UNIX) { -#ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); -#else - return CURLE_UNSUPPORTED_PROTOCOL; -#endif - } - else { /* TCP/UDP/QUIC */ -#ifdef USE_IPV6 - cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); -#endif - cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); - } return CURLE_OK; } @@ -458,15 +491,17 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(bs->max_concurrent) cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); - result = Curl_socket_addr_from_ai(&addr, ai, bs->transport); + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_peer); if(result) goto out; - result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, - bs->transport, bs->cf_create); + result = cf_ip_attempt_new(&a, data, cf, bs->origin, bs->peer, + bs->transport_peer, &addr, ai_family, + bs->tunnel_peer, bs->tunnel_transport, + bs->cf_create); CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d", bs->running ? "next" : "first", - (ai_family == AF_INET) ? "4" : "6", result); + (ai_family == AF_INET) ? "4" : "6", (int)result); if(result) goto out; DEBUGASSERT(a); @@ -495,7 +530,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(!a->inconclusive) continue; result = cf_ip_attempt_restart(a, cf, data); - CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result); + CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, (int)result); if(result) /* serious failure */ goto out; bs->last_attempt_started = *Curl_pgrs_now(data); @@ -518,7 +553,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, result = CURLE_COULDNT_CONNECT; VERBOSE(i = 0); for(a = bs->running; a; a = a->next) { - CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result); + CURL_TRC_CF(data, cf, "baller %d: result=%d", i, (int)a->result); if(a->result) result = a->result; } @@ -652,7 +687,6 @@ typedef enum { } cf_connect_state; struct cf_ip_happy_ctx { - uint8_t transport; cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; @@ -674,40 +708,39 @@ static CURLcode is_connected(struct Curl_cfilter *cf, if(!result) return CURLE_OK; else { - const char *hostname, *proxy_name = NULL; + struct Curl_peer *peer = NULL, *proxy_peer = NULL; char viamsg[160]; + + peer = Curl_conn_get_first_peer(conn, cf->sockindex); + if(!conn->origin || !peer) + return CURLE_FAILED_INIT; + #ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) - proxy_name = conn->socks_proxy.host.name; - else if(conn->bits.httpproxy) - proxy_name = conn->http_proxy.host.name; + if(conn->socks_proxy.peer) + proxy_peer = conn->socks_proxy.peer; + else if(conn->http_proxy.peer) + proxy_peer = conn->http_proxy.peer; #endif - hostname = conn->bits.conn_to_host ? conn->conn_to_host.name : - conn->host.name; + viamsg[0] = 0; + if(!Curl_peer_equal(peer, conn->origin) && + !Curl_peer_equal(peer, proxy_peer)) { #ifdef USE_UNIX_SOCKETS - if(conn->unix_domain_socket) - curl_msnprintf(viamsg, sizeof(viamsg), "over %s", - conn->unix_domain_socket); - else -#endif - { - uint16_t port; - if(cf->sockindex == SECONDARYSOCKET) - port = conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - port = conn->conn_to_port; + if(peer->unix_socket) + curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s", + peer->hostname); else - port = conn->remote_port; - curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); +#endif + curl_msnprintf(viamsg, sizeof(viamsg), " via %s:%u", + peer->hostname, peer->port); } - failf(data, "Failed to connect to %s %s %s%s%safter " + failf(data, "Failed to connect to %s:%u%s %s%s%safter " "%" FMT_TIMEDIFF_T " ms: %s", - hostname, viamsg, - proxy_name ? "via " : "", - proxy_name ? proxy_name : "", - proxy_name ? " " : "", + conn->origin->hostname, conn->origin->port, viamsg, + proxy_peer ? "over proxy " : "", + proxy_peer ? proxy_peer->hostname : "", + proxy_peer ? " " : "", curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle), curl_easy_strerror(result)); @@ -734,28 +767,41 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); + if(ctx->ballers.transport_peer == TRNSPRT_UNIX) { +#ifdef USE_UNIX_SOCKETS + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_UNIX); +#else + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + } + else { /* TCP/UDP/QUIC */ +#ifdef USE_IPV6 + cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, ctx->ballers.peer, AF_INET6); +#endif + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_INET); + } + + CURL_TRC_CF(data, cf, "init ip ballers for transport %u", + ctx->ballers.transport_peer); ctx->started = *Curl_pgrs_now(data); - return cf_ip_ballers_init(&ctx->ballers, cf, - ctx->cf_create, ctx->transport, - data->set.happy_eyeballs_timeout, - IP_HE_MAX_CONCURRENT_ATTEMPTS); + return CURLE_OK; } -static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, +static void cf_ip_happy_ctx_clear(struct cf_ip_happy_ctx *ctx, struct Curl_easy *data) { - struct cf_ip_happy_ctx *ctx = cf->ctx; - DEBUGASSERT(ctx); - DEBUGASSERT(data); - cf_ip_ballers_clear(cf, data, &ctx->ballers); + if(ctx) + cf_ip_ballers_clear(data, &ctx->ballers); } -static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx, + struct Curl_easy *data) { - if(ctx) + if(ctx) { + cf_ip_happy_ctx_clear(ctx, data); curlx_free(ctx); + } } static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, @@ -772,7 +818,7 @@ static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, } result = cf_ip_ballers_shutdown(&ctx->ballers, data, done); - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -785,7 +831,8 @@ static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, if(!cf->connected) { result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } @@ -810,7 +857,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->dns_resolved) { - result = Curl_conn_dns_result(cf->conn, cf->sockindex); + result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer); if(!result) ctx->dns_resolved = TRUE; else if(result == CURLE_AGAIN) { @@ -841,9 +888,9 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, cf->connected = TRUE; cf->next = ctx->ballers.winner->cf; ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_clear(ctx, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - /* whatever errors where reported by ballers, clear our errorbuf */ + /* whatever errors were reported by ballers, clear our errorbuf */ Curl_reset_fail(data); if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) @@ -871,22 +918,6 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, return result; } -static void cf_ip_happy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ip_happy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf_ip_happy_ctx_clear(cf, data); - cf->connected = FALSE; - ctx->state = SCFST_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -940,8 +971,8 @@ static void cf_ip_happy_destroy(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "destroy"); if(ctx) { - cf_ip_happy_ctx_clear(cf, data); - cf_ip_happy_ctx_destroy(ctx); + cf_ip_happy_ctx_clear(ctx, data); + cf_ip_happy_ctx_destroy(ctx, data); } } @@ -951,7 +982,6 @@ struct Curl_cftype Curl_cft_ip_happy = { CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, - cf_ip_happy_close, cf_ip_happy_shutdown, cf_ip_happy_adjust_pollset, cf_ip_happy_data_pending, @@ -963,21 +993,14 @@ struct Curl_cftype Curl_cft_ip_happy = { cf_ip_happy_query, }; -/** - * Create an IP happy eyeball connection filter that uses the, once resolved, - * address information to connect on ip families based on connection - * configuration. - * @param pcf output, the created cfilter - * @param data easy handle used in creation - * @param conn connection the filter is created for - * @param cf_create method to create the sub-filters performing the - * actual connects. - */ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, - cf_ip_connect_create *cf_create, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_ip_happy_ctx *ctx = NULL; CURLcode result; @@ -990,35 +1013,39 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport = transport; - ctx->cf_create = cf_create; + result = cf_ip_ballers_init(&ctx->ballers, data, + origin, peer, transport_peer, + tunnel_peer, tunnel_transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); + if(result) + goto out; result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx); out: if(result) { curlx_safefree(*pcf); - cf_ip_happy_ctx_destroy(ctx); + cf_ip_happy_ctx_destroy(ctx, data); } return result; } CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport) + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - cf_ip_connect_create *cf_create; struct Curl_cfilter *cf; CURLcode result; /* Need to be first */ DEBUGASSERT(cf_at); - cf_create = get_cf_create(transport); - if(!cf_create) { - CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport); + result = cf_ip_happy_create(&cf, data, origin, peer, transport_peer, + cf_at->conn, tunnel_peer, tunnel_transport); if(result) return result; diff --git a/deps/curl/lib/cf-ip-happy.h b/deps/curl/lib/cf-ip-happy.h index 547ee4b4ac..dd9b29ac8f 100644 --- a/deps/curl/lib/cf-ip-happy.h +++ b/deps/curl/lib/cf-ip-happy.h @@ -29,28 +29,44 @@ struct connectdata; struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; +struct Curl_peer; struct Curl_sockaddr_ex; /** - * Create a cfilter for making an "ip" connection to the - * given address, using parameters from `conn`. The "ip" connection - * can be a TCP socket, a UDP socket or even a QUIC connection. + * Create a cfilter to connect to `origin` via an optional `peer` + * using `transport_peer` and `addr`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. + * `pcf`: the filter created on success + * `data`: the transfer initiating the connect + * `conn`: the connection that gets connected * - * It MUST use only the supplied `ai` for its connection attempt. - * - * Such a filter may be used in "happy eyeball" scenarios, and its - * `connect` implementation needs to support non-blocking. Once connected, + * The filter is used in "happy eyeball" scenarios. Once connected, * it MAY be installed in the connection filter chain to serve transfers. */ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); +/** + * Create an IP happy eyeball connection filter that connects to `origin` + * via an optional `peer` using `transport_peer`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. + */ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport); + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_ip_happy; diff --git a/deps/curl/lib/cf-recvbuf.c b/deps/curl/lib/cf-recvbuf.c new file mode 100644 index 0000000000..8ccc148bd3 --- /dev/null +++ b/deps/curl/lib/cf-recvbuf.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ + +#include "urldata.h" +#include "bufq.h" +#include "cfilters.h" +#include "cf-recvbuf.h" +#include "curl_trc.h" + +#define CURL_CF_RECVBUF_CHUNK (16 * 1024) + +struct cf_recvbuf_ctx { + struct bufq recvbuf; +}; + +static void cf_recvbuf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } +} + +static CURLcode cf_recvbuf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) { + return Curl_bufq_cread(&ctx->recvbuf, buf, len, pnread); + } + + if(cf->next) + return cf->next->cft->do_recv(cf->next, data, buf, len, pnread); + *pnread = 0; + return CURLE_RECV_ERROR; +} + +static bool cf_recvbuf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +struct Curl_cftype Curl_cft_recvbuf = { + "RECVBUF", + 0, + CURL_LOG_LVL_NONE, + cf_recvbuf_destroy, + Curl_cf_def_connect, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + cf_recvbuf_data_pending, + Curl_cf_def_send, + cf_recvbuf_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf = NULL; + struct cf_recvbuf_ctx *ctx; + CURLcode result = CURLE_OK; + size_t nwritten = 0; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_bufq_init2(&ctx->recvbuf, CURL_CF_RECVBUF_CHUNK, + (blen / CURL_CF_RECVBUF_CHUNK) + 1, + (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES)); + result = Curl_bufq_write(&ctx->recvbuf, buf, blen, &nwritten); + if(result) + goto out; + if(nwritten != blen) { + result = CURLE_FAILED_INIT; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_recvbuf, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result ? NULL : cf; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_recvbuf_create(&cf, data, buf, blen); + if(result) + goto out; + + cf->connected = Curl_conn_is_connected(conn, sockindex); + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +#endif /* !CURL_DISABLE_WEBSOCKETS */ diff --git a/deps/curl/lib/noproxy.h b/deps/curl/lib/cf-recvbuf.h similarity index 69% rename from deps/curl/lib/noproxy.h rename to deps/curl/lib/cf-recvbuf.h index e16c139bb5..1b08fcc3be 100644 --- a/deps/curl/lib/noproxy.h +++ b/deps/curl/lib/cf-recvbuf.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_NOPROXY_H -#define HEADER_CURL_NOPROXY_H +#ifndef HEADER_CURL_CF_RECVBUF_H +#define HEADER_CURL_CF_RECVBUF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -25,8 +25,16 @@ ***************************************************************************/ #include "curl_setup.h" -#ifndef CURL_DISABLE_PROXY -bool Curl_check_noproxy(const char *name, const char *no_proxy); -#endif +#ifndef CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ -#endif /* HEADER_CURL_NOPROXY_H */ +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen); + +extern struct Curl_cftype Curl_cft_recvbuf; + +#endif /* !CURL_DISABLE_WEBSOCKETS */ + +#endif /* HEADER_CURL_CF_RECVBUF_H */ diff --git a/deps/curl/lib/cf-setup.c b/deps/curl/lib/cf-setup.c new file mode 100644 index 0000000000..119294a89a --- /dev/null +++ b/deps/curl/lib/cf-setup.c @@ -0,0 +1,476 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "cfilters.h" +#include "cf-haproxy.h" +#include "cf-ip-happy.h" +#include "cf-setup.h" +#include "curl_trc.h" +#include "connect.h" +#include "http_proxy.h" +#include "socks.h" +#include "vquic/cf-capsule.h" +#include "vquic/vquic.h" +#include "vtls/vtls.h" + + +typedef enum { + CF_SETUP_INIT, + CF_SETUP_CNNCT_EYEBALLS, + CF_SETUP_CNNCT_SOCKS, + CF_SETUP_CNNCT_HTTP_PROXY, + CF_SETUP_CNNCT_HAPROXY, + CF_SETUP_CNNCT_SSL, + CF_SETUP_DONE +} cf_setup_state; + +struct cf_setup_ctx { + cf_setup_state state; + int ssl_mode; + uint8_t transport; + uint8_t retry_count; +}; + +#ifndef CURL_DISABLE_PROXY + +static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { + if(data->set.haproxyprotocol) { + if(ctx->transport == TRNSPRT_QUIC) { + failf(data, "haproxy protocol does not support QUIC"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added HAPROXY filter"); + } + ctx->state = CF_SETUP_CNNCT_HAPROXY; + } + return result; +} + +static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) { + /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ + struct Curl_peer *second_peer; + + if(cf->conn->http_proxy.peer) + second_peer = cf->conn->http_proxy.peer; + else + second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if(!second_peer) + return CURLE_FAILED_INIT; + + result = Curl_cf_socks_proxy_insert_after( + cf, data, second_peer, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, + cf->conn->socks_proxy.creds); + if(result) { + CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result); + return result; + } + + CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", + second_peer->hostname, second_peer->port); + ctx->state = CF_SETUP_CNNCT_SOCKS; + } + return result; +} + +#ifndef CURL_DISABLE_HTTP +static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && + cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + struct Curl_peer *peer = cf->conn->http_proxy.peer; + struct Curl_peer *tunnel_peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + +#ifdef USE_SSL + if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) && + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + result = Curl_cf_ssl_proxy_insert_after( + cf, data, cf->conn->http_proxy.peer); + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); + } +#endif /* USE_SSL */ + + result = Curl_cf_http_proxy_insert_after( + cf, data, peer, tunnel_peer, + ctx->transport, cf->conn->http_proxy.proxytype); + if(result) { + CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + } + return result; +} +#endif /* !CURL_DISABLE_HTTP */ +#endif /* CURL_DISABLE_PROXY */ + +/* Get the origin curl connects its socket to. + * Can be origin or the first proxy. */ +static struct Curl_peer *conn_get_first_origin(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; +} + +static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { + /* What is the first hop we directly connect to and what transport + * do we use for it? Only on the first hop we can do Happy Eyeballs. + * first_origin and first_peer differ on --connect-to. */ + struct Curl_peer *first_origin = + conn_get_first_origin(cf->conn, cf->sockindex); + struct Curl_peer *first_peer = + Curl_conn_get_first_peer(cf->conn, cf->sockindex); + struct Curl_peer *tunnel_peer = NULL; + uint8_t first_transport = ctx->transport; + + if(!first_peer) + return CURLE_FAILED_INIT; + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + first_transport = + Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); + tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) { + failf(data, "HTTP/3 proxy not possible via SOCKS"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + } +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + + result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer, + first_transport, + tunnel_peer, ctx->transport); + if(result) { + CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result); + return result; + } + + if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) { + CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + } + else { + CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u", + tunnel_peer ? "proxy" : "origin", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_EYEBALLS; + } + } + return result; +} + +static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)data; /* not used in all builds */ + if(ctx->state < CF_SETUP_CNNCT_SSL) { +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + !defined(CURL_DISABLE_PROXY) + + /* Wanting QUIC with an HTTP tunneling filter, we now need to add + * the QUIC filter on top. Without tunneling, this has already + * happened in the Happy Eyeball filter. */ + if(ctx->transport == TRNSPRT_QUIC && + cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + + result = Curl_cf_capsule_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", + (int)result); + return result; + } + result = Curl_cf_quic_insert_after(cf, origin, peer); + if(result) { + CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added QUIC filter for origin"); + } + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */ +#ifdef USE_SSL + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || + (ctx->ssl_mode != CURL_CF_SSL_DISABLE && + cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + +#ifndef CURL_DISABLE_PROXY + if(cf->conn->bits.origin_is_proxy) { + result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin); + } + else +#endif + { + /* Another FTP quirk: when adding SSL verification, to a DATA + * connection, always verify against the control's origin */ + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + result = Curl_cf_ssl_insert_after(cf, data, origin, peer); + } + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for origin"); + } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; + } + return result; +} + +static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* connect current sub-chain */ +connect_sub_chain: + VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); + + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + result = cf_setup_add_ip_happy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_PROXY + result = cf_setup_add_socks(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_HTTP + result = cf_setup_add_http_proxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_HTTP */ + + result = cf_setup_add_haproxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_PROXY */ + + result = cf_setup_add_origin_filters(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + + ctx->state = CF_SETUP_DONE; + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; +} + +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result; + + /* In some situations, a server/proxy may close the connection and + * we need to connect again (HTTP/1.x proxy auth, for example). + * We used to close the filters and reuse them for another attempt, + * however that complicates filter code and it is simpler to tear them + * all down and start over. */ +retry: + result = cf_setup_connect_steps(cf, data, done); + + if(result == CURLE_AGAIN) { + ++ctx->retry_count; + if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */ + return CURLE_COULDNT_CONNECT; + + CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count); + Curl_conn_cf_discard_chain(&cf->next, data); + ctx->state = CF_SETUP_INIT; + goto retry; + } + return result; +} + +static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + curlx_safefree(ctx); +} + +struct Curl_cftype Curl_cft_setup = { + "SETUP", + CF_TYPE_SETUP, + CURL_LOG_LVL_NONE, + cf_setup_destroy, + cf_setup_connect, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_setup_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf = NULL; + struct cf_setup_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->state = CF_SETUP_INIT; + ctx->ssl_mode = ssl_mode; + ctx->transport = transport; + + result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result ? NULL : cf; + if(ctx) { + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + return result; +} diff --git a/deps/curl/lib/cf-setup.h b/deps/curl/lib/cf-setup.h new file mode 100644 index 0000000000..80e20064b8 --- /dev/null +++ b/deps/curl/lib/cf-setup.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_CF_SETUP_H +#define HEADER_CURL_CF_SETUP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_dns_entry; +struct ip_quadruple; +struct Curl_peer; +struct Curl_str; + +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t transport, + int ssl_mode); + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode); + +extern struct Curl_cftype Curl_cft_setup; + +#endif /* HEADER_CURL_CF_SETUP_H */ diff --git a/deps/curl/lib/cf-socket.c b/deps/curl/lib/cf-socket.c index b99bcdef55..0741f2ba41 100644 --- a/deps/curl/lib/cf-socket.c +++ b/deps/curl/lib/cf-socket.c @@ -26,6 +26,9 @@ #ifdef HAVE_NETINET_IN_H #include /* may need it */ #endif +#ifdef HAVE_SYS_UN_H +#include /* for sockaddr_un */ +#endif #ifdef HAVE_LINUX_TCP_H #include #elif defined(HAVE_NETINET_TCP_H) @@ -44,16 +47,17 @@ #include #endif +#ifdef HAVE_IFADDRS_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif #ifdef __VMS #include #include #endif -#ifdef __DragonFly__ -/* Required for __DragonFly_version */ -#include -#endif - #include "urldata.h" #include "curl_trc.h" #include "if2ip.h" @@ -63,11 +67,13 @@ #include "curl_addrinfo.h" #include "select.h" #include "multiif.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "progress.h" #include "conncache.h" #include "multihandle.h" #include "rand.h" +#include "sockaddr.h" #include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" @@ -77,6 +83,63 @@ #include "curlx/strparse.h" +/* retrieves ip address and port from a sockaddr structure. note it calls + * curlx_inet_ntop which sets errno on fail, not SOCKERRNO. + * @unittest 1607 + */ +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port); +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port) +{ + struct sockaddr_in *si = NULL; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = NULL; +#endif +#ifdef USE_UNIX_SOCKETS + struct sockaddr_un *su = NULL; +#else + (void)salen; +#endif + + switch(sa->sa_family) { + case AF_INET: + si = (struct sockaddr_in *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si->sin_port); + return TRUE; + } + break; +#ifdef USE_IPV6 + case AF_INET6: + si6 = (struct sockaddr_in6 *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si6->sin6_port); + return TRUE; + } + break; +#endif +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { + su = (struct sockaddr_un *)sa; + curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ + *port = 0; + return TRUE; +#endif + default: + break; + } + + addr[0] = '\0'; + *port = 0; + errno = SOCKEAFNOSUPPORT; + return FALSE; +} + static void tcpnodelay(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t sockfd) @@ -86,8 +149,7 @@ static void tcpnodelay(struct Curl_cfilter *cf, int level = IPPROTO_TCP; VERBOSE(char buffer[STRERROR_LEN]); - if(setsockopt(sockfd, level, TCP_NODELAY, - (void *)&onoff, sizeof(onoff)) < 0) + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); #else @@ -97,8 +159,8 @@ static void tcpnodelay(struct Curl_cfilter *cf, #endif } -#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ - defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ +#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ + defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD) #if defined(USE_WINSOCK) || \ (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ @@ -132,14 +194,15 @@ static void tcpkeepalive(struct Curl_cfilter *cf, VERSION_GREATER_THAN_EQUAL)) { CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd); optval = curlx_sltosi(data->set.tcp_keepidle); -/* Offered by mingw-w64 v12+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */ #ifndef TCP_KEEPALIVE #define TCP_KEEPALIVE 3 #endif -/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef TCP_KEEPCNT #define TCP_KEEPCNT 16 #endif +/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #ifndef TCP_KEEPIDLE #define TCP_KEEPIDLE TCP_KEEPALIVE #endif @@ -302,6 +365,49 @@ int Curl_sock_nosigpipe(curl_socket_t sockfd) } #endif /* USE_SO_NOSIGPIPE */ +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +static uint32_t get_scope_id(struct Curl_easy *data, + struct sockaddr_in6 *sa6) +{ + uint32_t scope_id = 0; + if(data->conn->scope_id) + return data->conn->scope_id; + /* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */ + scope_id = sa6->sin6_scope_id; + if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + /* The resolver did not set scope_id for this link-local address. + * Try to determine it from the system's network interfaces. + * Without a scope_id, connect() to a link-local address fails + * with EINVAL on Linux. + * NOTE: On multi-homed hosts with several interfaces having + * link-local addresses, this picks the first one found, which + * may not be the correct outgoing interface. */ +#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H) + struct ifaddrs *ifa, *ifa_list; + if(getifaddrs(&ifa_list) == 0) { + for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) { + if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 && + (ifa->ifa_flags & IFF_UP) && + !(ifa->ifa_flags & IFF_LOOPBACK)) { + struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr; + if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) { + scope_id = s6->sin6_scope_id; + infof(data, + "determined scope_id=%lu for link-local address " + "from local interface", + (unsigned long)scope_id); + break; + } + } + } + freeifaddrs(ifa_list); + } +#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */ + } + return scope_id; +} +#endif + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) @@ -371,9 +477,9 @@ static CURLcode socket_open(struct Curl_easy *data, #endif #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(data->conn->scope_id && (addr->family == AF_INET6)) { + if(addr->family == AF_INET6) { struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr; - sa6->sin6_scope_id = data->conn->scope_id; + sa6->sin6_scope_id = get_scope_id(data, sa6); } #endif return CURLE_OK; @@ -511,8 +617,7 @@ CURLcode Curl_parse_interface(const char *input, ++host_part; *host = curlx_memdup0(host_part, len - (host_part - input)); if(!*host) { - curlx_free(*iface); - *iface = NULL; + curlx_safefree(*iface); return CURLE_OUT_OF_MEMORY; } return CURLE_OK; @@ -547,7 +652,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, const char *host_input = data->set.str[STRING_BINDHOST]; const char *iface = iface_input ? iface_input : dev; const char *host = host_input ? host_input : dev; - int error; + int sockerr; #ifdef IP_BIND_ADDRESS_NO_PORT int on = 1; #endif @@ -610,9 +715,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, if(iface_input && !host_input) { /* Do not fall back to treating it as a hostname */ char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "Could not bind to interface '%s' with errno %d: %s", - iface, error, curlx_strerror(error, buffer, sizeof(buffer))); + iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } break; @@ -624,7 +729,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, * We now have the numerical IP address in the 'myhost' buffer */ host = myhost; - infof(data, "Local Interface %s is ip %s using address family %i", + infof(data, "Local Interface %s is ip %s using address family %d", iface, host, af); done = 1; break; @@ -650,7 +755,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); - infof(data, "Name '%s' family %i resolved to '%s' family %i", + infof(data, "Name '%s' family %d resolved to '%s' family %d", host, af, myhost, h_af); Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { @@ -714,9 +819,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, generic resolve error. */ char buffer[STRERROR_LEN]; data->state.errorbuf = FALSE; - data->state.os_errno = error = SOCKERRNO; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "Could not bind to '%s' with errno %d: %s", host, - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } } @@ -765,9 +870,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, } { char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "bind failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); } return CURLE_INTERFACE_FAILED; @@ -777,12 +882,12 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* * verifyconnect() returns TRUE if the connect really has happened. */ -static bool verifyconnect(curl_socket_t sockfd, int *error) +static bool verifyconnect(curl_socket_t sockfd, int *psockerr) { bool rc = TRUE; #ifdef SO_ERROR - int err = 0; - curl_socklen_t errSize = sizeof(err); + int sockerr = 0; + curl_socklen_t errSize = sizeof(sockerr); #ifdef _WIN32 /* @@ -803,27 +908,27 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) SleepEx(0, FALSE); #endif - if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = SOCKERRNO; + if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errSize)) + sockerr = SOCKERRNO; #if defined(EBADIOCTL) && defined(__minix) /* Minix 3.1.x does not support getsockopt on UDP sockets */ - if(EBADIOCTL == err) { + if(EBADIOCTL == sockerr) { SET_SOCKERRNO(0); - err = 0; + sockerr = 0; } #endif - if((err == 0) || (SOCKEISCONN == err)) + if((sockerr == 0) || (SOCKEISCONN == sockerr)) /* we are connected, awesome! */ rc = TRUE; else /* This was not a successful connect */ rc = FALSE; - if(error) - *error = err; + if(psockerr) + *psockerr = sockerr; #else (void)sockfd; - if(error) - *error = SOCKERRNO; + if(psockerr) + *psockerr = SOCKERRNO; #endif return rc; } @@ -832,38 +937,25 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * Determine the curl code for a socket connect() == -1 with errno. */ static CURLcode socket_connect_result(struct Curl_easy *data, - const char *ipaddress, int error) + const char *ipaddress, int sockerr) { - switch(error) { - case SOCKEINPROGRESS: - case SOCKEWOULDBLOCK: -#ifdef EAGAIN -#if (EAGAIN) != (SOCKEWOULDBLOCK) - /* On some platforms EAGAIN and EWOULDBLOCK are the - * same value, and on others they are different, hence - * the odd #if - */ - case EAGAIN: -#endif -#endif + if(sockerr == SOCKEINPROGRESS || SOCK_EAGAIN(sockerr)) return CURLE_OK; - default: - /* unknown error, fallthrough and try another address! */ - { - VERBOSE(char buffer[STRERROR_LEN]); - infof(data, "Immediate connect fail for %s: %s", ipaddress, - curlx_strerror(error, buffer, sizeof(buffer))); - NOVERBOSE((void)ipaddress); - } - data->state.os_errno = error; - /* connect failed */ - return CURLE_COULDNT_CONNECT; + /* unknown error, fallthrough and try another address! */ + { + VERBOSE(char buffer[STRERROR_LEN]); + infof(data, "Immediate connect fail for %s: %s", ipaddress, + curlx_strerror(sockerr, buffer, sizeof(buffer))); + NOVERBOSE((void)ipaddress); } + data->state.os_errno = sockerr; + /* connect failed */ + return CURLE_COULDNT_CONNECT; } struct cf_socket_ctx { - uint8_t transport; + struct Curl_peer *peer; struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ @@ -874,13 +966,14 @@ struct cf_socket_ctx { struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */ ULONG sndbuf_size; /* the last set SO_SNDBUF size */ #endif - int error; /* errno of last failure or 0 */ + int sockerr; /* socket error of last failure or 0 */ #ifdef DEBUGBUILD int wblock_percent; /* percent of writes doing EAGAIN */ int wpartial_percent; /* percent of bytes written in send */ int rblock_percent; /* percent of reads doing EAGAIN */ size_t recv_max; /* max enforced read size */ #endif + uint8_t transport; BIT(got_first_byte); /* if first byte was received */ BIT(listening); /* socket is listening */ BIT(accepted); /* socket was accepted, not connected */ @@ -889,10 +982,12 @@ struct cf_socket_ctx { }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, + struct Curl_peer *peer, struct Curl_sockaddr_ex *addr, uint8_t transport) { memset(ctx, 0, sizeof(*ctx)); + Curl_peer_link(&ctx->peer, peer); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; ctx->addr = *addr; @@ -929,22 +1024,12 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, return CURLE_OK; } -static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cf_socket_ctx_free(struct cf_socket_ctx *ctx) { - struct cf_socket_ctx *ctx = cf->ctx; - - if(ctx && ctx->sock != CURL_SOCKET_BAD) { - CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); - if(ctx->sock == cf->conn->sock[cf->sockindex]) - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - ctx->active = FALSE; - memset(&ctx->started_at, 0, sizeof(ctx->started_at)); - memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); + if(ctx) { + Curl_peer_unlink(&ctx->peer); + curlx_free(ctx); } - - cf->connected = FALSE; } static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, @@ -973,10 +1058,16 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; - cf_socket_close(cf, data); CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); - cf->ctx = NULL; + if(ctx) { + if(ctx->sock != CURL_SOCKET_BAD) { + CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); + if(ctx->sock == cf->conn->sock[cf->sockindex]) + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + } + cf_socket_ctx_free(ctx); + } } static void set_local_ip(struct Curl_cfilter *cf, @@ -996,12 +1087,12 @@ static void set_local_ip(struct Curl_cfilter *cf, memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { - VERBOSE(int error = SOCKERRNO); + VERBOSE(int sockerr = SOCKERRNO); infof(data, "getsockname() failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); } - else if(!Curl_addr2string((struct sockaddr *)&ssloc, slen, - ctx->ip.local_ip, &ctx->ip.local_port)) { + else if(!sockaddr2string((struct sockaddr *)&ssloc, slen, + ctx->ip.local_ip, &ctx->ip.local_port)) { infof(data, "ssloc inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); } @@ -1018,12 +1109,15 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, /* store remote address and port used in this connection attempt */ ctx->ip.transport = ctx->transport; - if(!Curl_addr2string(&ctx->addr.curl_sa_addr, - (curl_socklen_t)ctx->addr.addrlen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { + if(!sockaddr2string(&ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { char buffer[STRERROR_LEN]; - ctx->error = errno; + /* using bare errno instead of SOCKERRNO is safe here, because + sockaddr2string() calls curlx_inet_ntop(), and they both report failures + via errno (even on Windows builds). */ + ctx->sockerr = errno; /* malformed address or bug in inet_ntop, try next address */ failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); @@ -1091,7 +1185,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); #endif - infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + { + struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr; + if(sa6->sin6_scope_id) + infof(data, " Trying [%s]:%d scope_id=%lu...", + ctx->ip.remote_ip, ctx->ip.remote_port, + (unsigned long)sa6->sin6_scope_id); + else +#endif + infof(data, " Trying [%s]:%d...", + ctx->ip.remote_ip, ctx->ip.remote_port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + } +#endif } else #endif @@ -1154,7 +1261,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, error = curlx_nonblock(ctx->sock, TRUE); if(error < 0) { result = CURLE_UNSUPPORTED_PROTOCOL; - ctx->error = SOCKERRNO; + ctx->sockerr = SOCKERRNO; goto out; } #else @@ -1164,7 +1271,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, error = curlx_nonblock(ctx->sock, TRUE); if(error < 0) { result = CURLE_UNSUPPORTED_PROTOCOL; - ctx->error = SOCKERRNO; + ctx->sockerr = SOCKERRNO; goto out; } } @@ -1183,7 +1290,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, cf->connected = TRUE; } CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T, - result, ctx->sock); + (int)result, ctx->sock); return result; } @@ -1257,7 +1364,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, *done = FALSE; /* a negative world view is best */ if(ctx->sock == CURL_SOCKET_BAD) { - int error; + int sockerr; result = cf_socket_open(cf, data); if(result) @@ -1270,12 +1377,13 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, /* Connect TCP socket */ rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen); - error = SOCKERRNO; + sockerr = SOCKERRNO; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "local address %s port %d...", ctx->ip.local_ip, ctx->ip.local_port); if(rc == -1) { - result = socket_connect_result(data, ctx->ip.remote_ip, error); + ctx->sockerr = sockerr; + result = socket_connect_result(data, ctx->ip.remote_ip, sockerr); goto out; } } @@ -1295,7 +1403,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, return CURLE_OK; } else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { - if(verifyconnect(ctx->sock, &ctx->error)) { + if(verifyconnect(ctx->sock, &ctx->sockerr)) { /* we are connected with TCP, awesome! */ ctx->connected_at = *Curl_pgrs_now(data); set_local_ip(cf, data); @@ -1307,7 +1415,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, } else if(rc & CURL_CSELECT_ERR) { CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock); - (void)verifyconnect(ctx->sock, &ctx->error); + (void)verifyconnect(ctx->sock, &ctx->sockerr); result = CURLE_COULDNT_CONNECT; } @@ -1315,10 +1423,10 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, if(result) { VERBOSE(char buffer[STRERROR_LEN]); set_local_ip(cf, data); - if(ctx->error) { - data->state.os_errno = ctx->error; - SET_SOCKERRNO(ctx->error); - VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer))); + if(ctx->sockerr) { + data->state.os_errno = ctx->sockerr; + SET_SOCKERRNO(ctx->sockerr); + VERBOSE(curlx_strerror(ctx->sockerr, buffer, sizeof(buffer))); } else { VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed"))); @@ -1330,7 +1438,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, infof(data, "connect to %s port %u from %s port %d failed: %s", ctx->ip.remote_ip, ctx->ip.remote_port, ctx->ip.local_ip, ctx->ip.local_port, - curlx_strerror(ctx->error, buffer, sizeof(buffer))); + curlx_strerror(ctx->sockerr, buffer, sizeof(buffer))); *done = FALSE; } return result; @@ -1369,7 +1477,7 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf, #ifdef USE_WINSOCK -/* Offered by mingw-w64 v13+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v13+, MS SDK 7.0A/VS2010+ */ #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif @@ -1442,22 +1550,12 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!curlx_sztouz(rv, pnwritten)) { int sockerr = SOCKERRNO; - - if( -#ifdef USE_WINSOCK - /* This is how Windows does it */ - (SOCKEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (SOCKEWOULDBLOCK == sockerr) || - (EAGAIN == sockerr) || (SOCKEINTR == sockerr) || - (SOCKEINPROGRESS == sockerr) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) #endif ) { - /* EWOULDBLOCK */ - result = CURLE_AGAIN; + result = CURLE_AGAIN; /* EWOULDBLOCK */ } else { char buffer[STRERROR_LEN]; @@ -1474,7 +1572,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #endif CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu", - orig_len, result, *pnwritten); + orig_len, (int)result, *pnwritten); cf->conn->sock[cf->sockindex] = fdsave; return result; } @@ -1508,21 +1606,12 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!curlx_sztouz(rv, pnread)) { int sockerr = SOCKERRNO; - - if( -#ifdef USE_WINSOCK - /* This is how Windows does it */ - (SOCKEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (SOCKEWOULDBLOCK == sockerr) || - (EAGAIN == sockerr) || (SOCKEINTR == sockerr) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) #endif ) { - /* EWOULDBLOCK */ - result = CURLE_AGAIN; + result = CURLE_AGAIN; /* EWOULDBLOCK */ } else { char buffer[STRERROR_LEN]; @@ -1533,7 +1622,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } - CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread); + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread); if(!result && !ctx->got_first_byte) { ctx->first_byte_at = *Curl_pgrs_now(data); ctx->got_first_byte = TRUE; @@ -1548,8 +1637,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf, if(cf->connected && (cf->sockindex == FIRSTSOCKET)) { struct cf_socket_ctx *ctx = cf->ctx; data->info.primary = ctx->ip; - /* not sure if this is redundant... */ - data->info.conn_remote_port = cf->conn->remote_port; } } @@ -1696,7 +1783,6 @@ struct Curl_cftype Curl_cft_tcp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1710,17 +1796,24 @@ struct Curl_cftype Curl_cft_tcp = { CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_TCP); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_TCP); if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; @@ -1732,7 +1825,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1742,7 +1835,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1837,7 +1930,8 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); if(result) { - CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", + (int)result); goto out; } @@ -1862,7 +1956,6 @@ struct Curl_cftype Curl_cft_udp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_udp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1876,24 +1969,31 @@ struct Curl_cftype Curl_cft_udp = { CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1903,7 +2003,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1916,7 +2016,6 @@ struct Curl_cftype Curl_cft_unix = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1929,25 +2028,32 @@ struct Curl_cftype Curl_cft_unix = { }; CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UNIX); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UNIX); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1957,7 +2063,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -2005,13 +2111,13 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, plen = sizeof(ssrem); memset(&ssrem, 0, plen); if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) { - int error = SOCKERRNO; + int sockerr = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return; } - if(!Curl_addr2string((struct sockaddr *)&ssrem, plen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { + if(!sockaddr2string((struct sockaddr *)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); return; @@ -2058,7 +2164,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); socketstate = SOCKET_READABLE(ctx->sock, 0); - CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate); + CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate); switch(socketstate) { case -1: /* error */ /* let's die here */ @@ -2128,10 +2234,10 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, int error = 0; /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); + Curl_set_in_callback(data, TRUE); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, FALSE); if(error) return CURLE_ABORTED_BY_CALLBACK; @@ -2146,7 +2252,6 @@ struct Curl_cftype Curl_cft_tcp_accept = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_accept_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, diff --git a/deps/curl/lib/cf-socket.h b/deps/curl/lib/cf-socket.h index 40c001cc14..37ddc02576 100644 --- a/deps/curl/lib/cf-socket.h +++ b/deps/curl/lib/cf-socket.h @@ -25,8 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ - struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; @@ -34,23 +32,6 @@ struct connectdata; struct Curl_sockaddr_ex; struct ip_quadruple; -/* - * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr - * structure with enough space available to directly hold any - * protocol-specific address structures. The variable declared here will be - * used to pass / receive data to/from the fopensocket callback if this has - * been set, before that, it is initialized from parameters. - */ -struct Curl_sockaddr_ex { - int family; - int socktype; - int protocol; - unsigned int addrlen; - union { - struct sockaddr sa; - struct Curl_sockaddr_storage buf; - } addr; -}; #define curl_sa_addr addr.sa #define curl_sa_addrbuf addr.buf @@ -94,9 +75,13 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, */ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UDP socket to the given address @@ -107,9 +92,13 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UNIX socket to the given address @@ -120,9 +109,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that keeps a listening socket. diff --git a/deps/curl/lib/cfilters.c b/deps/curl/lib/cfilters.c index 6d7d8ef738..fb1914d133 100644 --- a/deps/curl/lib/cfilters.c +++ b/deps/curl/lib/cfilters.c @@ -33,16 +33,26 @@ #include "select.h" #include "curlx/strparse.h" -#ifdef UNITTESTS -/* @unittest 2600 */ -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; } -#endif CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -117,29 +127,28 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, } #ifdef CURLVERBOSE -static void conn_trc_filters(struct Curl_easy *data, - int sockindex, - const char *info) +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info) { if(CURL_TRC_M_is_verbose(data) && data->conn) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; if(cf) { - struct dynbuf msg; - CURLcode result = CURLE_OK; - - curlx_dyn_init(&msg, 1024); - result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); - for(; cf && !result; cf = cf->next) { - result = curlx_dyn_addf(&msg, "[%s%s]", - cf->connected ? "" : "!", cf->cft->name); + char msg[256], *buf; + int blen, n; + + buf = msg; + blen = sizeof(msg) - 1; + n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex); + buf += n; + blen -= n; + for(; cf && blen; cf = cf->next) { + n = curl_msnprintf(buf, blen, "[%s%s]", + cf->connected ? "" : "!", cf->cft->name); + buf += n; + blen -= n; } - if(!result) - CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); - else - CURL_TRC_M(data, "%s [%d] error %d tracing chain", - info, sockindex, result); - curlx_dyn_free(&msg); + CURL_TRC_M(data, "%s%s", msg, blen ? "" : "..."); } else CURL_TRC_M(data, "%s [%d][-]", info, sockindex); @@ -170,22 +179,11 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, void Curl_conn_cf_discard_all(struct Curl_easy *data, struct connectdata *conn, int sockindex) { + struct curltime *pt = &conn->shutdown.start[sockindex]; + memset(pt, 0, sizeof(*pt)); Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -void Curl_conn_close(struct Curl_easy *data, int sockindex) -{ - struct Curl_cfilter *cf; - - DEBUGASSERT(data->conn); - /* it is valid to call that without filters being present */ - cf = data->conn->cfilter[sockindex]; - if(cf) { - cf->cft->do_close(cf, data); - } - Curl_shutdown_clear(data, sockindex); -} - CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) { struct Curl_cfilter *cf; @@ -225,7 +223,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) bool cfdone = FALSE; result = cf->cft->do_shutdown(cf, data, &cfdone); if(result) { - CURL_TRC_CF(data, cf, "shut down failed with %d", result); + CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result); return result; } else if(!cfdone) { @@ -427,12 +425,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf) - cf->cft->do_close(cf, data); -} - CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) @@ -452,29 +444,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_RECV_ERROR; } -#ifdef CURLVERBOSE -static CURLcode cf_verboseconnect(struct Curl_easy *data, - struct Curl_cfilter *cf) -{ - if(Curl_trc_is_verbose(data)) { - struct ip_quadruple ipquad; - bool is_ipv6; - CURLcode result; - - result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); - if(result) - return result; - - infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", - (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", - CURL_CONN_HOST_DISPNAME(data->conn), - ipquad.remote_ip, ipquad.remote_port, - ipquad.local_ip, ipquad.local_port); - } - return CURLE_OK; -} -#endif - static CURLcode cf_cntrl_all(struct connectdata *conn, struct Curl_easy *data, bool ignore_result, @@ -492,36 +461,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, return result; } -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn) +void Curl_conn_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn) { cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); } -/** - * Update connection statistics - */ -static void conn_report_connect_stats(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - if(cf) { - struct curltime connected; - struct curltime appconnected; - - memset(&connected, 0, sizeof(connected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); - if(connected.tv_sec || connected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); - - memset(&appconnected, 0, sizeof(appconnected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); - if(appconnected.tv_sec || appconnected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); - } -} - -static void conn_remove_setup_filters(struct Curl_easy *data, - int sockindex) +void Curl_conn_remove_setup_filters(struct Curl_easy *data, + int sockindex) { struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; while(*anchor) { @@ -538,123 +485,11 @@ static void conn_remove_setup_filters(struct Curl_easy *data, } } -CURLcode Curl_conn_connect(struct Curl_easy *data, - int sockindex, - bool blocking, - bool *done) -{ -#define CF_CONN_NUM_POLLS_ON_STACK 5 - struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; - struct easy_pollset ps; - struct curl_pollfds cpfds; - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - if(!CONN_SOCK_IDX_VALID(sockindex)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->conn->scheme->flags & PROTOPT_NONETWORK) { - *done = TRUE; - return CURLE_OK; - } - - cf = data->conn->cfilter[sockindex]; - if(!cf) { - *done = FALSE; - return CURLE_FAILED_INIT; - } - - *done = (bool)cf->connected; - if(*done) - return CURLE_OK; - - Curl_pollset_init(&ps); - Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); - while(!*done) { - if(Curl_conn_needs_flush(data, sockindex)) { - DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); - result = Curl_conn_flush(data, sockindex); - if(result && (result != CURLE_AGAIN)) - return result; - } - - result = cf->cft->do_connect(cf, data, done); - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", - blocking, result, *done); - if(!result && *done) { - /* Now that the complete filter chain is connected, let all filters - * persist information at the connection. E.g. cf-socket sets the - * socket and ip related information. */ - cf_cntrl_update_info(data, data->conn); - conn_report_connect_stats(cf, data); - data->conn->keepalive = *Curl_pgrs_now(data); - VERBOSE(result = cf_verboseconnect(data, cf)); - VERBOSE(conn_trc_filters(data, sockindex, "connected")); - conn_remove_setup_filters(data, sockindex); - VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); - VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); - conn_report_connect_stats(cf, data); - goto out; - } - - if(!blocking) - goto out; - else { - /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft_ms(data); - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - int rc; - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "connect timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; - } - - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); - Curl_pollset_reset(&ps); - Curl_pollfds_reset(&cpfds); - /* In general, we want to send after connect, wait on that. */ - if(sockfd != CURL_SOCKET_BAD) - result = Curl_pollset_set_out_only(data, &ps, sockfd); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, &ps); - if(result) - goto out; - result = Curl_pollfds_add_ps(&cpfds, &ps); - if(result) - goto out; - - rc = Curl_poll(cpfds.pfds, cpfds.n, - CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", - rc); - if(rc < 0) { - result = CURLE_COULDNT_CONNECT; - goto out; - } - /* continue iterating */ - } - } - -out: - Curl_pollset_cleanup(&ps); - Curl_pollfds_cleanup(&cpfds); - return result; -} - bool Curl_conn_is_setup(struct connectdata *conn, int sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; - return (conn->cfilter[sockindex] != NULL); + return !!conn->cfilter[sockindex]; } bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) @@ -688,10 +523,37 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) return FALSE; } +#ifndef CURL_DISABLE_PROXY +static bool cf_is_tunneling(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if((cf->cft->flags & CF_TYPE_PROXY)) + return TRUE; + } + return FALSE; +} + +bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +{ + if(!CONN_SOCK_IDX_VALID(sockindex)) + return FALSE; + return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE; +} +#else +bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +{ + (void)conn; + (void)sockindex; + return FALSE; +} +#endif /* CURL_DISABLE_PROXY */ + static bool cf_is_ssl(struct Curl_cfilter *cf) { for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_SSL) + /* A tunneling proxy does not offer end2end encryption, even if + * it does SSL itself (e.g. QUIC H3 proxy) */ + if((cf->cft->flags & CF_TYPE_SSL) && !(cf->cft->flags & CF_TYPE_PROXY)) return TRUE; if(cf->cft->flags & CF_TYPE_IP_CONNECT) return FALSE; @@ -890,8 +752,19 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(conn); - for(i = 0; (i < 2) && !result; ++i) { - result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + /* During connect time, connection filters may add sockets to the pollset + * even when the transfer neither wants to send nor receive. And those + * sockets, when having events, are served. + * Once connected however, a transfer that neither wants to send nor receive + * will never call the connection filters. Any sockets added by the filters + * will not change state and POLLIN/POLLOUT events will trigger forever, + * making us busy loop. See #21671 */ + if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) || + (conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(conn, SECONDARYSOCKET))) { + for(i = 0; (i < 2) && !result && conn; ++i) { + result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + } } return result; } @@ -953,8 +826,8 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ - *phost = data->conn->host.name; - portarg = data->conn->remote_port; + *phost = data->conn->origin->hostname; + portarg = data->conn->origin->port; } if(pport) *pport = portarg; @@ -1018,8 +891,8 @@ const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, return NULL; } -static const struct Curl_sockaddr_ex * -cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data) +static const struct Curl_sockaddr_ex *cf_get_remote_addr( + struct Curl_cfilter *cf, struct Curl_easy *data) { const struct Curl_sockaddr_ex *remote_addr = NULL; if(cf && @@ -1057,8 +930,8 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data) return data->conn->sock[FIRSTSOCKET]; } -const struct Curl_sockaddr_ex * -Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) +const struct Curl_sockaddr_ex *Curl_conn_get_remote_addr( + struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf = (data->conn && CONN_SOCK_IDX_VALID(sockindex)) ? diff --git a/deps/curl/lib/cfilters.h b/deps/curl/lib/cfilters.h index ac56737ccb..b1aad029e0 100644 --- a/deps/curl/lib/cfilters.h +++ b/deps/curl/lib/cfilters.h @@ -39,10 +39,6 @@ struct curl_tlssessioninfo; typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data); -/* Callback to close the connection immediately. */ -typedef void Curl_cft_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - /* Callback to close the connection filter gracefully, non-blocking. * Implementations MUST NOT chain calls to cf->next. */ @@ -218,7 +214,6 @@ struct Curl_cftype { int log_level; /* log level for such filters */ Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ Curl_cft_connect *do_connect; /* establish connection */ - Curl_cft_close *do_close; /* close conn */ Curl_cft_shutdown *do_shutdown; /* shutdown conn */ Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ Curl_cft_data_pending *has_data_pending; /* conn has data pending */ @@ -270,6 +265,8 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, int query, int *pres1, void *pres2); CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); /** * Create a new filter instance, unattached to the filter chain. @@ -322,7 +319,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); @@ -364,16 +360,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 -/** - * Bring the filter chain at `sockindex` for connection `data->conn` into - * connected state. Which will set `*done` to TRUE. - * This can be called on an already connected chain with no side effects. - * When not `blocking`, calls may return without error and `*done != TRUE`, - * while the individual filters negotiated the connection. - */ -CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, - bool blocking, bool *done); - /** * Check if a filter chain at `sockindex` for connection `conn` exists. */ @@ -399,6 +385,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); */ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); +/* Determine if the connection has one or more proxy filters. + * e.g. is tunneling. */ +bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex); + /* * Fill `info` with information about the TLS instance securing the connection * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return @@ -433,11 +423,11 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data, const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn); -/** - * Close the filter chain at `sockindex` for connection `data->conn`. - * Filters remain in place and may be connected again afterwards. - */ -void Curl_conn_close(struct Curl_easy *data, int sockindex); +void Curl_conn_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn); + +void Curl_conn_remove_setup_filters(struct Curl_easy *data, + int sockindex); /** * Shutdown the connection at `sockindex` non-blocking, using timeout @@ -582,7 +572,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, * Get the remote hostname and port that the connection is currently * talking to (or will talk to). * Once connected or before connect starts, - * it is `conn->host.name` and `conn->remote_port`. + * it is `conn->origin->hostname` and `conn->origin->port`. * During connect, when tunneling proxies are involved (http or socks), * it will be the name and port the proxy currently negotiates with. */ @@ -604,6 +594,11 @@ int Curl_conn_get_stream_error(struct Curl_easy *data, struct connectdata *conn, int sockindex); +#ifdef CURLVERBOSE +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info); +#endif + /** * Get the index of the given socket in the connection's sockets. * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the @@ -664,7 +659,7 @@ struct cf_call_data { #define CF_DATA_SAVE(save, cf, data) \ do { \ (save) = CF_CTX_CALL_DATA(cf); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf).depth++; \ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \ } while(0) @@ -672,7 +667,7 @@ struct cf_call_data { #define CF_DATA_RESTORE(cf, save) \ do { \ DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf) = (save); \ } while(0) diff --git a/deps/curl/lib/config-os400.h b/deps/curl/lib/config-os400.h index 3d8d59c97e..76841af202 100644 --- a/deps/curl/lib/config-os400.h +++ b/deps/curl/lib/config-os400.h @@ -34,11 +34,10 @@ /* Global configuration parameters: normally generated by autoconf. */ /* ---------------------------------------------------------------- */ -/* Location of default ca bundle */ +/* Location of default CA bundle */ /* Use the system keyring as the default CA bundle. */ #define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" - /* Definition to make a library symbol externally visible. */ #define CURL_EXTERN_SYMBOL @@ -239,7 +238,7 @@ /* Size of time_t in number of bytes */ #define SIZEOF_TIME_T 4 -/* Define to 1 if all of the C89 standard headers exist (not just the ones +/* Define to 1 if all of the C89 standard headers exist (not only the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #define STDC_HEADERS 1 diff --git a/deps/curl/lib/conncache.c b/deps/curl/lib/conncache.c index 33fb68b124..5ba2368622 100644 --- a/deps/curl/lib/conncache.c +++ b/deps/curl/lib/conncache.c @@ -122,7 +122,7 @@ void Curl_cpool_init(struct cpool *cpool, cpool->idata = idata; cpool->share = share; - cpool->initialised = TRUE; + cpool->initialized = TRUE; } /* Return the "first" connection in the pool or NULL. */ @@ -230,7 +230,7 @@ static void cpool_discard_conn(struct cpool *cpool, void Curl_cpool_destroy(struct cpool *cpool) { - if(cpool && cpool->initialised && cpool->idata) { + if(cpool && cpool->initialized && cpool->idata) { struct connectdata *conn; struct Curl_sigpipe_ctx pipe_ctx; @@ -305,9 +305,9 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool, return bundle; } -static struct connectdata * -cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle, - const struct curltime *pnow) +static struct connectdata *cpool_bundle_get_oldest_idle( + struct cpool_bundle *bundle, + const struct curltime *pnow) { struct Curl_llist_node *curr; timediff_t highscore = -1; diff --git a/deps/curl/lib/conncache.h b/deps/curl/lib/conncache.h index 7cee4d4729..71940d724c 100644 --- a/deps/curl/lib/conncache.h +++ b/deps/curl/lib/conncache.h @@ -56,7 +56,7 @@ struct cpool { struct Curl_easy *idata; /* internal handle for maintenance */ struct Curl_share *share; /* != NULL if pool belongs to share */ BIT(locked); - BIT(initialised); + BIT(initialized); }; /* Init the pool, pass multi only if pool is owned by it. diff --git a/deps/curl/lib/connect.c b/deps/curl/lib/connect.c index b13d496848..a2b86c7458 100644 --- a/deps/curl/lib/connect.c +++ b/deps/curl/lib/connect.c @@ -23,51 +23,20 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef HAVE_NETINET_IN_H -#include /* may need it */ -#endif -#ifdef HAVE_SYS_UN_H -#include /* for sockaddr_un */ -#endif -#ifdef HAVE_LINUX_TCP_H -#include -#elif defined(HAVE_NETINET_TCP_H) -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif - -#ifdef __VMS -#include -#include -#endif - #include "urldata.h" #include "curl_trc.h" #include "strerror.h" #include "cfilters.h" #include "connect.h" #include "cf-dns.h" -#include "cf-haproxy.h" #include "cf-https-connect.h" -#include "cf-ip-happy.h" -#include "cf-socket.h" +#include "cf-setup.h" #include "multiif.h" -#include "curlx/inet_ntop.h" -#include "curlx/strparse.h" -#include "vtls/vtls.h" /* for vtls cfilters */ #include "progress.h" #include "conncache.h" #include "multihandle.h" -#include "http_proxy.h" -#include "socks.h" +#include "select.h" +#include "curlx/strparse.h" #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) @@ -210,59 +179,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) return FALSE; } -/* retrieves ip address and port from a sockaddr structure. note it calls - curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */ -bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port) -{ - struct sockaddr_in *si = NULL; -#ifdef USE_IPV6 - struct sockaddr_in6 *si6 = NULL; -#endif -#ifdef USE_UNIX_SOCKETS - struct sockaddr_un *su = NULL; -#else - (void)salen; -#endif - - switch(sa->sa_family) { - case AF_INET: - si = (struct sockaddr_in *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { - *port = ntohs(si->sin_port); - return TRUE; - } - break; -#ifdef USE_IPV6 - case AF_INET6: - si6 = (struct sockaddr_in6 *)(void *)sa; - if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { - *port = ntohs(si6->sin6_port); - return TRUE; - } - break; -#endif -#ifdef USE_UNIX_SOCKETS - case AF_UNIX: - if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { - su = (struct sockaddr_un *)sa; - curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); - } - else - addr[0] = 0; /* socket with no name */ - *port = 0; - return TRUE; -#endif - default: - break; - } - - addr[0] = '\0'; - *port = 0; - errno = SOCKEAFNOSUPPORT; - return FALSE; -} - /* * Used to extract socket and connectdata struct for the most recent * transfer on the given Curl_easy. @@ -325,267 +241,22 @@ void Curl_conncontrol(struct connectdata *conn, } } -typedef enum { - CF_SETUP_INIT, - CF_SETUP_CNNCT_EYEBALLS, - CF_SETUP_CNNCT_SOCKS, - CF_SETUP_CNNCT_HTTP_PROXY, - CF_SETUP_CNNCT_HAPROXY, - CF_SETUP_CNNCT_SSL, - CF_SETUP_DONE -} cf_setup_state; - -struct cf_setup_ctx { - cf_setup_state state; - int ssl_mode; - uint8_t transport; -}; - -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct cf_setup_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* connect current sub-chain */ -connect_sub_chain: - - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - result = cf_ip_happy_insert_after(cf, data, ctx->transport); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_EYEBALLS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - /* sub-chain connected, do we need to add more? */ -#ifndef CURL_DISABLE_PROXY - if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - const char *hostname = - cf->conn->bits.httpproxy ? - cf->conn->http_proxy.host.name : - cf->conn->bits.conn_to_host ? - cf->conn->conn_to_host.name : - cf->sockindex == SECONDARYSOCKET ? - cf->conn->secondaryhostname : cf->conn->host.name; - uint16_t port = - cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : - cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : - cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : - cf->conn->remote_port; - const char *user = cf->conn->socks_proxy.user; - const char *passwd = cf->conn->socks_proxy.passwd; - - result = Curl_cf_socks_proxy_insert_after( - cf, data, hostname, port, cf->conn->ip_version, - cf->conn->socks_proxy.proxytype, user, passwd); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_SOCKS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { -#ifdef USE_SSL - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && - !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* USE_SSL */ - -#ifndef CURL_DISABLE_HTTP - if(cf->conn->bits.tunnel_proxy) { - result = Curl_cf_http_proxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* !CURL_DISABLE_HTTP */ - ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } -#endif /* !CURL_DISABLE_PROXY */ - - if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { -#ifndef CURL_DISABLE_PROXY - if(data->set.haproxyprotocol) { - if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not supported with SSL " - "encryption in place (QUIC?)"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = Curl_cf_haproxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* !CURL_DISABLE_PROXY */ - ctx->state = CF_SETUP_CNNCT_HAPROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - if(ctx->state < CF_SETUP_CNNCT_SSL) { -#ifdef USE_SSL - if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || - (ctx->ssl_mode != CURL_CF_SSL_DISABLE && - cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ - !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ - result = Curl_cf_ssl_insert_after(cf, data); - if(result) - return result; - } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - ctx->state = CF_SETUP_DONE; - cf->connected = TRUE; - *done = TRUE; - return CURLE_OK; -} - -static void cf_setup_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - ctx->state = CF_SETUP_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - -static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "destroy"); - curlx_safefree(ctx); -} - -struct Curl_cftype Curl_cft_setup = { - "SETUP", - CF_TYPE_SETUP, - CURL_LOG_LVL_NONE, - cf_setup_destroy, - cf_setup_connect, - cf_setup_close, - Curl_cf_def_shutdown, - Curl_cf_def_adjust_pollset, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -static CURLcode cf_setup_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf = NULL; - struct cf_setup_ctx *ctx; - CURLcode result = CURLE_OK; - - (void)data; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->state = CF_SETUP_INIT; - ctx->ssl_mode = ssl_mode; - ctx->transport = transport; - - result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); - if(result) - goto out; - ctx = NULL; - -out: - *pcf = result ? NULL : cf; - if(ctx) { - curlx_free(ctx); - } - return result; -} - -static CURLcode cf_setup_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); -out: - return result; -} - -CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_insert_after(cf_at, cf); -out: - return result; -} - CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode) { CURLcode result = CURLE_OK; + struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); DEBUGASSERT(!conn->cfilter[sockindex]); + if(!peer) + return CURLE_FAILED_INIT; + #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && conn->scheme->protocol == CURLPROTO_HTTPS) { @@ -598,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, /* Still no cfilter set, apply default. */ if(!conn->cfilter[sockindex]) { - result = cf_setup_add(data, conn, sockindex, - conn->transport_wanted, ssl_mode); + result = Curl_cf_setup_add(data, conn, sockindex, + conn->transport_wanted, ssl_mode); if(result) goto out; } @@ -609,28 +280,179 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, if(sockindex == FIRSTSOCKET) dns_queries |= CURL_DNSQ_HTTPS; #endif - result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, - conn->transport_wanted, dns); + result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, + conn->transport_wanted); DEBUGASSERT(conn->cfilter[sockindex]); out: return result; } -#ifdef USE_UNIX_SOCKETS -const char *Curl_conn_get_unix_path(struct connectdata *conn) +#ifdef CURLVERBOSE +static CURLcode conn_connect_trace(struct Curl_easy *data, + struct Curl_cfilter *cf) { - const char *unix_path = conn->unix_domain_socket; + if(Curl_trc_is_verbose(data)) { + struct ip_quadruple ipquad; + bool is_ipv6; + CURLcode result; -#ifndef CURL_DISABLE_PROXY - if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name && - !strncmp(UNIX_SOCKET_PREFIX "/", - conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) - unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; + result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); + if(result) + return result; + + infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", + CURL_CONN_HOST_DISPNAME(data->conn), + ipquad.remote_ip, ipquad.remote_port, + ipquad.local_ip, ipquad.local_port); + } + return CURLE_OK; +} #endif - return unix_path; +/** + * Update connection statistics + */ +static void conn_report_connect_stats(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf) { + struct curltime connected; + struct curltime appconnected; + + memset(&connected, 0, sizeof(connected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); + if(connected.tv_sec || connected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); + + memset(&appconnected, 0, sizeof(appconnected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); + if(appconnected.tv_sec || appconnected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + } +} + +CURLcode Curl_conn_connect(struct Curl_easy *data, + int sockindex, + bool blocking, + bool *done) +{ +#define CF_CONN_NUM_POLLS_ON_STACK 5 + struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; + struct easy_pollset ps; + struct curl_pollfds cpfds; + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + if(!CONN_SOCK_IDX_VALID(sockindex)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { + *done = TRUE; + return CURLE_OK; + } + + cf = data->conn->cfilter[sockindex]; + if(!cf) { + *done = FALSE; + return CURLE_FAILED_INIT; + } + + *done = (bool)cf->connected; + if(*done) + return CURLE_OK; + + Curl_pollset_init(&ps); + Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); + while(!*done) { + if(Curl_conn_needs_flush(data, sockindex)) { + DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); + result = Curl_conn_flush(data, sockindex); + if(result && (result != CURLE_AGAIN)) + goto out; + } + + result = cf->cft->do_connect(cf, data, done); + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", + blocking, (int)result, *done); + if(!result && *done) { + /* A final sanity check on connection security */ + if((data->state.origin->scheme->flags & PROTOPT_SSL) && + (sockindex == FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { + DEBUGASSERT(0); + failf(data, "transfer requires SSL, but not connected via SSL"); + result = CURLE_FAILED_INIT; + goto out; + } + /* Now that the complete filter chain is connected, let all filters + * persist information at the connection. E.g. cf-socket sets the + * socket and ip related information. */ + Curl_conn_cntrl_update_info(data, data->conn); + conn_report_connect_stats(cf, data); + data->conn->keepalive = *Curl_pgrs_now(data); + VERBOSE(result = conn_connect_trace(data, cf)); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); + Curl_conn_remove_setup_filters(data, sockindex); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", + (int)result); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); + conn_report_connect_stats(cf, data); + goto out; + } + + if(!blocking) + goto out; + else { + /* check allowed time left */ + const timediff_t timeout_ms = Curl_timeleft_ms(data); + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + int rc; + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "connect timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); + Curl_pollset_reset(&ps); + Curl_pollfds_reset(&cpfds); + /* In general, we want to send after connect, wait on that. */ + if(sockfd != CURL_SOCKET_BAD) + result = Curl_pollset_set_out_only(data, &ps, sockfd); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, &ps); + if(result) + goto out; + result = Curl_pollfds_add_ps(&cpfds, &ps); + if(result) + goto out; + + rc = Curl_poll(cpfds.pfds, cpfds.n, + CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", + rc); + if(rc < 0) { + result = CURLE_COULDNT_CONNECT; + goto out; + } + /* continue iterating */ + } + } + +out: + Curl_pollset_cleanup(&ps); + Curl_pollfds_cleanup(&cpfds); + return result; } -#endif /* USE_UNIX_SOCKETS */ void Curl_conn_set_multiplex(struct connectdata *conn) { @@ -641,3 +463,32 @@ void Curl_conn_set_multiplex(struct connectdata *conn) } } } + +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex) +{ + return (sockindex == SECONDARYSOCKET) ? + conn->origin2 : conn->origin; +} + +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex) +{ + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} + +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} diff --git a/deps/curl/lib/connect.h b/deps/curl/lib/connect.h index 40f1c9c572..95bde1eb4c 100644 --- a/deps/curl/lib/connect.h +++ b/deps/curl/lib/connect.h @@ -28,8 +28,7 @@ #include "hostip.h" #include "curlx/timeval.h" -struct Curl_dns_entry; -struct ip_quadruple; +struct Curl_peer; struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); @@ -71,9 +70,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); -bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port); - /* * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' * argument specifies if it is the end of a connection or a stream. @@ -86,9 +82,9 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, * when the connection will close. */ -#define CONNCTRL_KEEP 0 /* undo a marked closure */ +#define CONNCTRL_KEEP 0 /* undo a marked closure */ #define CONNCTRL_CONNECTION 1 -#define CONNCTRL_STREAM 2 +#define CONNCTRL_STREAM 2 void Curl_conncontrol(struct connectdata *conn, int ctrl @@ -99,42 +95,50 @@ void Curl_conncontrol(struct connectdata *conn, #if defined(DEBUGBUILD) && defined(CURLVERBOSE) #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) +#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y) +#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y) #else /* !DEBUGBUILD || !CURLVERBOSE */ #define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM) -#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) -#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) +#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION) +#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) #endif -CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t transport, - int ssl_mode); - /** * Setup the cfilters at `sockindex` in connection `conn`. * If no filter chain is installed yet, inspects the configuration - * in `data` and `conn? to install a suitable filter chain. + * in `data` and `conn` to install a suitable filter chain. */ CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode); +/** + * Bring the filter chain at `sockindex` for connection `data->conn` into + * connected state. Which will set `*done` to TRUE. + * This can be called on an already connected chain with no side effects. + * When not `blocking`, calls may return without error and `*done != TRUE`, + * while the individual filters negotiated the connection. + */ +CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, + bool blocking, bool *done); + /* Set conn to allow multiplexing. */ void Curl_conn_set_multiplex(struct connectdata *conn); -#ifdef USE_UNIX_SOCKETS -#ifndef CURL_DISABLE_PROXY -#define UNIX_SOCKET_PREFIX "localhost" -#endif -const char *Curl_conn_get_unix_path(struct connectdata *conn); -#else -#define Curl_conn_get_unix_path(c) NULL -#endif +/* Get the origin peer at sockindex. */ +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex); + +/* Get the peer the connection actually connects to at sockindex. + * Often the same as "origin", but can be redirected via "connect-to" + * or "alt-svc". May tunnel through proxies. */ +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex); -extern struct Curl_cftype Curl_cft_setup; +/* Get the peer curl connects its socket to. + * Can be origin, "connect-to" or the first proxy. */ +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex); #endif /* HEADER_CURL_CONNECT_H */ diff --git a/deps/curl/lib/content_encoding.c b/deps/curl/lib/content_encoding.c index 0224a8bfe9..fd08077259 100644 --- a/deps/curl/lib/content_encoding.c +++ b/deps/curl/lib/content_encoding.c @@ -46,6 +46,7 @@ #include #endif +#include "connect.h" #include "sendf.h" #include "curl_trc.h" #include "content_encoding.h" @@ -88,7 +89,7 @@ static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void)opaque; /* not a typo, keep it curlx_calloc() */ - return (voidpf)curlx_calloc(items, size); + return curlx_calloc(items, size); } static void zfree_cb(voidpf opaque, voidpf ptr) @@ -100,8 +101,7 @@ static void zfree_cb(voidpf opaque, voidpf ptr) static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) - failf(data, "Error while processing content unencoding: %s", - z->msg); + failf(data, "Error while processing content unencoding: %s", z->msg); else failf(data, "Error while processing content unencoding: " "Unknown failure within decompression software."); @@ -154,6 +154,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, z_const Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ + int i = 0; /* Check state. */ if(zp->zlib_init != ZLIB_INIT && @@ -164,9 +165,18 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* because the buffer size is fixed, iteratively decompress and transfer to the client via next_write function. */ while(!done) { - int status; /* zlib status */ + int status; /* zlib status */ done = TRUE; + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return exit_zlib(data, z, &zp->zlib_init, CURLE_OPERATION_TIMEDOUT); + } + } + /* (re)set buffer for decompressed output for every iteration */ z->next_out = (Bytef *)zp->buffer; z->avail_out = DECOMPRESS_BUFFER_SIZE; @@ -176,7 +186,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Flush output data if some. */ if(z->avail_out != DECOMPRESS_BUFFER_SIZE) { if(status == Z_OK || status == Z_STREAM_END) { - zp->zlib_init = started; /* Data started. */ + zp->zlib_init = started; /* Data started. */ result = Curl_cwriter_write(data, writer->next, type, zp->buffer, DECOMPRESS_BUFFER_SIZE - z->avail_out); if(result) { @@ -210,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = FALSE; break; } - zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ + zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; @@ -224,7 +234,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) - zp->zlib_init = started; /* Cannot restart anymore. */ + zp->zlib_init = started; /* Cannot restart anymore. */ return result; } @@ -234,7 +244,7 @@ static CURLcode deflate_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -251,7 +261,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -271,7 +281,7 @@ static void deflate_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -293,7 +303,7 @@ static CURLcode gzip_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -311,7 +321,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -332,7 +342,7 @@ static void gzip_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -353,7 +363,7 @@ static const struct Curl_cwtype gzip_encoding = { struct brotli_writer { struct Curl_cwriter super; char buffer[DECOMPRESS_BUFFER_SIZE]; - BrotliDecoderState *br; /* State structure for brotli. */ + BrotliDecoderState *br; /* State structure for brotli. */ }; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) @@ -412,15 +422,26 @@ static CURLcode brotli_do_write(struct Curl_easy *data, size_t dstleft; CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(!bp->br) - return CURLE_WRITE_ERROR; /* Stream already ended. */ + return CURLE_WRITE_ERROR; /* Stream already ended. */ while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { + + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + dst = (uint8_t *)bp->buffer; dstleft = DECOMPRESS_BUFFER_SIZE; r = BrotliDecoderDecompressStream(bp->br, @@ -473,7 +494,7 @@ static const struct Curl_cwtype brotli_encoding = { /* Zstd writer. */ struct zstd_writer { struct Curl_cwriter super; - ZSTD_DStream *zds; /* State structure for zstd. */ + ZSTD_DStream *zds; /* State structure for zstd. */ char buffer[DECOMPRESS_BUFFER_SIZE]; }; @@ -520,6 +541,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -529,6 +551,15 @@ static CURLcode zstd_do_write(struct Curl_easy *data, in.size = nbytes; for(;;) { + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + out.pos = 0; out.dst = zp->buffer; out.size = DECOMPRESS_BUFFER_SIZE; @@ -750,24 +781,14 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_OK; } - if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %d content encodings", - MAX_ENCODE_STACK); + if(Curl_cwriter_count(data, phase) >= MAX_ENCODE_STACK) { + failf(data, "Reject response exceeding limit of %d %s encodings", + MAX_ENCODE_STACK, + is_transfer ? "transfer" : "content"); return CURLE_BAD_CONTENT_ENCODING; } cwt = find_unencode_writer(name, namelen, phase); - if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { - /* A 'chunked' transfer encoding has already been added. - * Ignore duplicates. See #13451. - * Also RFC 9112, ch. 6.1: - * "A sender MUST NOT apply the chunked transfer coding more than - * once to a message body." - */ - CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); - return CURLE_OK; - } - if(is_transfer && !is_chunked && Curl_cwriter_get_by_name(data, "chunked")) { /* RFC 9112, ch. 6.1: @@ -782,20 +803,31 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, "Transfer-Encoding"); return CURLE_BAD_CONTENT_ENCODING; } - - if(!cwt) - cwt = &error_writer; /* Defer error at use. */ - - result = Curl_cwriter_create(&writer, data, cwt, phase); - CURL_TRC_WRITE(data, "added %s decoder %s -> %d", - is_transfer ? "transfer" : "content", cwt->name, result); - if(result) - return result; - - result = Curl_cwriter_add(data, writer); - if(result) { - Curl_cwriter_free(data, writer); - return result; + if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { + /* A 'chunked' transfer encoding has already been added. + * Ignore duplicates. See #13451. + * Also RFC 9112, ch. 6.1: + * "A sender MUST NOT apply the chunked transfer coding more than + * once to a message body." + */ + CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); + } + else { + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ + + result = Curl_cwriter_create(&writer, data, cwt, phase); + CURL_TRC_WRITE(data, "added %s decoder %s -> %d", + is_transfer ? "transfer" : "content", cwt->name, + (int)result); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } } if(is_chunked) has_chunked = TRUE; diff --git a/deps/curl/lib/cookie.c b/deps/curl/lib/cookie.c index 0f822ea7cf..91dc8d5fd0 100644 --- a/deps/curl/lib/cookie.c +++ b/deps/curl/lib/cookie.c @@ -29,6 +29,7 @@ #include "cookie.h" #include "psl.h" #include "curl_trc.h" +#include "transfer.h" #include "slist.h" #include "curl_share.h" #include "strcase.h" @@ -71,7 +72,7 @@ static void freecookie(struct Cookie *co, bool maintoo) } static bool cookie_tailmatch(const char *cookie_domain, - size_t cookie_domain_len, + const size_t cookie_domain_len, const char *hostname) { size_t hostname_len = strlen(hostname); @@ -250,19 +251,11 @@ static char *sanitize_cookie_path(const char *cookie_path, size_t len) /* * strstore * - * A thin wrapper around strdup which ensures that any memory allocated at - * *str will be freed before the string allocated by strdup is stored there. - * The intended usecase is repeated assignments to the same variable during - * parsing in a last-wins scenario. The caller is responsible for checking - * for OOM errors. + * A thin wrapper around curlx_memdup0(). */ static CURLcode strstore(char **str, const char *newstr, size_t len) { DEBUGASSERT(str); - if(!len) { - len++; - newstr = ""; - } *str = curlx_memdup0(newstr, len); if(!*str) return CURLE_OUT_OF_MEMORY; @@ -354,9 +347,9 @@ static bool bad_domain(const char *domain, size_t len) static bool invalid_octets(const char *ptr, size_t len) { const unsigned char *p = (const unsigned char *)ptr; - /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ + /* Reject all bytes \x01 - \x1f + \x7f */ while(len && *p) { - if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f)) + if((*p < 0x20) || (*p == 0x7f)) return TRUE; p++; len--; @@ -377,7 +370,7 @@ static bool invalid_octets(const char *ptr, size_t len) #define COOKIE_PIECES 4 /* the list above */ -static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, +static CURLcode storecookie(struct Cookie *co, const struct Curl_str *cp, const char *path, const char *domain) { CURLcode result; @@ -421,19 +414,189 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, return result; } -/* this function return errors on OOM etc, not on plain cookie format - problems */ -static CURLcode parse_cookie_header( - struct Curl_easy *data, - struct Cookie *co, - struct CookieInfo *ci, - bool *okay, /* if the cookie was fine */ - const char *ptr, - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is - set, used to get default path for - the cookie unless set */ - bool secure) /* TRUE if connection is over secure origin */ +/* + * Parse the first name/value pair of the cookie header, which is the actual + * cookie name and value. + */ +static bool parse_first_pair(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie, + struct Curl_str *name, + struct Curl_str *val, + bool sep) +{ + /* The first name/value pair is the actual cookie name */ + if(!sep || !curlx_strlen(name)) { + infof(data, "invalid cookie, dropped"); + return FALSE; + } + + /* + * Check for too long individual name or contents. Chrome and Firefox + * support 4095 or 4096 bytes combo + */ + if((curlx_strlen(name) + curlx_strlen(val)) > MAX_NAME) { + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + curlx_strlen(name), curlx_strlen(val)); + return FALSE; + } + + /* Check if we have a reserved prefix set. */ + if(!strncmp("__Secure-", curlx_str(name), 9)) + co->prefix_secure = TRUE; + else if(!strncmp("__Host-", curlx_str(name), 7)) + co->prefix_host = TRUE; + + cookie[COOKIE_NAME] = *name; + cookie[COOKIE_VALUE] = *val; + return TRUE; +} + +static bool parse_flag(struct Curl_easy *data, struct Cookie *co, + const struct CookieInfo *ci, + struct Curl_str *name, bool secure) +{ + /* + * secure cookies are only allowed to be set when the connection is + * using a secure protocol, or when the cookie is being set by + * reading from file + */ + if(curlx_str_casecompare(name, "secure")) { + if(secure || !ci->running) + co->secure = TRUE; + else { + infof(data, "skipped cookie because not 'secure'"); + return FALSE; + } + } + else if(curlx_str_casecompare(name, "httponly")) + co->httponly = TRUE; + + return TRUE; +} + +static bool parse_domain(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie_domain, + struct Curl_str *val, + const char **domainp) +{ + bool is_ip; + const char *domain = *domainp; + const char *v = curlx_str(val); + /* + * Now, we make sure that our host is within the given domain, or + * the given domain is not valid and thus cannot be set. + */ + + if('.' == *v) + curlx_str_nudge(val, 1); + +#ifndef USE_LIBPSL + /* + * Without PSL we do not know when the incoming cookie is set on a + * TLD or otherwise "protected" suffix. To reduce risk, we require a + * dot OR the exact hostname being "localhost". + */ + if(bad_domain(curlx_str(val), curlx_strlen(val))) { + *domainp = ":"; + domain = ":"; + } +#endif + + is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(val)); + + if(!domain || + (is_ip && + !strncmp(curlx_str(val), domain, curlx_strlen(val)) && + (curlx_strlen(val) == strlen(domain))) || + (!is_ip && cookie_tailmatch(curlx_str(val), + curlx_strlen(val), domain))) { + *cookie_domain = *val; + if(!is_ip) + co->tailmatch = TRUE; /* we always do that if the domain name was + given */ + } + else { + /* + * We did not get a tailmatch and then the attempted set domain is + * not a domain to which the current host belongs. Mark as bad. + */ + infof(data, "skipped cookie with bad tailmatch domain: %s", + curlx_str(val)); + return FALSE; + } + return TRUE; +} + +static void parse_maxage(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + int rc; + const char *maxage = curlx_str(val); + if(*maxage == '\"') + maxage++; + rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); + if(!*nowp) + *nowp = time(NULL); + switch(rc) { + case STRE_OVERFLOW: + /* overflow, used max value */ + co->expires = CURL_OFF_T_MAX; + break; + default: + /* negative or otherwise bad, expire */ + co->expires = 1; + break; + case STRE_OK: + if(!co->expires) + co->expires = 1; /* expire now */ + else if(CURL_OFF_T_MAX - *nowp < co->expires) + /* would overflow */ + co->expires = CURL_OFF_T_MAX; + else + co->expires += *nowp; + break; + } + cap_expires(*nowp, co); +} + +static void parse_expires(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + /* + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie + */ + if(!co->expires && (curlx_strlen(val) < MAX_DATE_LENGTH)) { + char dbuf[MAX_DATE_LENGTH + 1]; + time_t date = 0; + memcpy(dbuf, curlx_str(val), curlx_strlen(val)); + dbuf[curlx_strlen(val)] = 0; + if(!Curl_getdate_capped(dbuf, &date)) { + if(!date) + date++; + co->expires = (curl_off_t)date; + } + else + co->expires = 0; + if(!*nowp) + *nowp = time(NULL); + cap_expires(*nowp, co); + } +} + +/* this function returns errors on OOM etc, not for cookie format problems */ +static CURLcode +parse_cookie_header(struct Curl_easy *data, + struct Cookie *co, + const struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, /* the header */ + const char *domain, /* default domain */ + /* full path used when this cookie is set */ + const char *path, + bool secure_origin) { /* This line was read off an HTTP-header */ time_t now = 0; @@ -449,22 +612,25 @@ static CURLcode parse_cookie_header( memset(cookie, 0, sizeof(cookie)); do { struct Curl_str name; - struct Curl_str val; /* we have a = pair or a stand-alone word here */ if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) { + struct Curl_str val; bool sep = FALSE; curlx_str_trimblanks(&name); + if(invalid_octets(curlx_str(&name), curlx_strlen(&name))) { + infof(data, "invalid octets in name, cookie dropped"); + return CURLE_OK; + } + if(!curlx_str_single(&ptr, '=')) { sep = TRUE; /* a '=' was used */ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); - /* Reject cookies with a TAB inside the value */ - if(curlx_strlen(&val) && - memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { - infof(data, "cookie contains TAB, dropping"); + if(invalid_octets(curlx_str(&val), curlx_strlen(&val))) { + infof(data, "invalid octets in value, cookie dropped"); return CURLE_OK; } } @@ -472,167 +638,23 @@ static CURLcode parse_cookie_header( curlx_str_init(&val); if(!curlx_strlen(&cookie[COOKIE_NAME])) { - /* The first name/value pair is the actual cookie name */ - if(!sep || - /* Bad name/value pair. */ - invalid_octets(curlx_str(&name), curlx_strlen(&name)) || - invalid_octets(curlx_str(&val), curlx_strlen(&val)) || - !curlx_strlen(&name)) { - infof(data, "invalid octets in name/value, cookie dropped"); + if(!parse_first_pair(data, co, cookie, &name, &val, sep)) return CURLE_OK; - } - - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(curlx_strlen(&name) >= (MAX_NAME - 1) || - curlx_strlen(&val) >= (MAX_NAME - 1) || - ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) { - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - curlx_strlen(&name), curlx_strlen(&val)); - return CURLE_OK; - } - - /* Check if we have a reserved prefix set. */ - if(!strncmp("__Secure-", curlx_str(&name), 9)) - co->prefix_secure = TRUE; - else if(!strncmp("__Host-", curlx_str(&name), 7)) - co->prefix_host = TRUE; - - cookie[COOKIE_NAME] = name; - cookie[COOKIE_VALUE] = val; } else if(!sep) { - /* - * this is a "" with no content - */ - - /* - * secure cookies are only allowed to be set when the connection is - * using a secure protocol, or when the cookie is being set by - * reading from file - */ - if(curlx_str_casecompare(&name, "secure")) { - if(secure || !ci->running) - co->secure = TRUE; - else { - infof(data, "skipped cookie because not 'secure'"); - return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "httponly")) - co->httponly = TRUE; + if(!parse_flag(data, co, ci, &name, secure_origin)) + return CURLE_OK; } - else if(curlx_str_casecompare(&name, "path")) { + else if(curlx_str_casecompare(&name, "path")) cookie[COOKIE_PATH] = val; - } else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) { - bool is_ip; - const char *v = curlx_str(&val); - /* - * Now, we make sure that our host is within the given domain, or - * the given domain is not valid and thus cannot be set. - */ - - if('.' == *v) - curlx_str_nudge(&val, 1); - -#ifndef USE_LIBPSL - /* - * Without PSL we do not know when the incoming cookie is set on a - * TLD or otherwise "protected" suffix. To reduce risk, we require a - * dot OR the exact hostname being "localhost". - */ - if(bad_domain(curlx_str(&val), curlx_strlen(&val))) - domain = ":"; -#endif - - is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val)); - - if(!domain || - (is_ip && - !strncmp(curlx_str(&val), domain, curlx_strlen(&val)) && - (curlx_strlen(&val) == strlen(domain))) || - (!is_ip && cookie_tailmatch(curlx_str(&val), - curlx_strlen(&val), domain))) { - cookie[COOKIE_DOMAIN] = val; - if(!is_ip) - co->tailmatch = TRUE; /* we always do that if the domain name was - given */ - } - else { - /* - * We did not get a tailmatch and then the attempted set domain is - * not a domain to which the current host belongs. Mark as bad. - */ - infof(data, "skipped cookie with bad tailmatch domain: %s", - curlx_str(&val)); + if(!parse_domain(data, co, &cookie[COOKIE_DOMAIN], &val, &domain)) return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) { - /* - * Defined in RFC2109: - * - * Optional. The Max-Age attribute defines the lifetime of the - * cookie, in seconds. The delta-seconds value is a decimal non- - * negative integer. After delta-seconds seconds elapse, the - * client should discard the cookie. A value of zero means the - * cookie should be discarded immediately. - */ - int rc; - const char *maxage = curlx_str(&val); - if(*maxage == '\"') - maxage++; - rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); - if(!now) - now = time(NULL); - switch(rc) { - case STRE_OVERFLOW: - /* overflow, used max value */ - co->expires = CURL_OFF_T_MAX; - break; - default: - /* negative or otherwise bad, expire */ - co->expires = 1; - break; - case STRE_OK: - if(!co->expires) - co->expires = 1; /* expire now */ - else if(CURL_OFF_T_MAX - now < co->expires) - /* would overflow */ - co->expires = CURL_OFF_T_MAX; - else - co->expires += now; - break; - } - cap_expires(now, co); - } - else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val) && - !co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) { - /* - * Let max-age have priority. - * - * If the date cannot get parsed for whatever reason, the cookie - * will be treated as a session cookie - */ - char dbuf[MAX_DATE_LENGTH + 1]; - time_t date = 0; - memcpy(dbuf, curlx_str(&val), curlx_strlen(&val)); - dbuf[curlx_strlen(&val)] = 0; - if(!Curl_getdate_capped(dbuf, &date)) { - if(!date) - date++; - co->expires = (curl_off_t)date; - } - else - co->expires = 0; - if(!now) - now = time(NULL); - cap_expires(now, co); } + else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) + parse_maxage(co, &val, &now); + else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) + parse_expires(co, &val, &now); } } while(!curlx_str_single(&ptr, ';')); @@ -646,11 +668,10 @@ static CURLcode parse_cookie_header( } static CURLcode parse_netscape(struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool *okay, const char *lineptr, - bool secure) /* TRUE if connection is over - secure origin */ + bool secure_origin) { /* * This line is NOT an HTTP header style line, we do offer support for @@ -667,7 +688,7 @@ static CURLcode parse_netscape(struct Cookie *co, * Firefox's cookie files, they are prefixed #HttpOnly_ and the rest * remains as usual, so we skip 10 characters of the line. */ - if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { + if(!strncmp(lineptr, "#HttpOnly_", 10)) { lineptr += 10; co->httponly = TRUE; } @@ -723,7 +744,7 @@ static CURLcode parse_netscape(struct Cookie *co, case 3: co->secure = FALSE; if(curl_strnequal(ptr, "TRUE", len)) { - if(secure || ci->running) + if(secure_origin || ci->running) co->secure = TRUE; else return CURLE_OK; @@ -738,10 +759,12 @@ static CURLcode parse_netscape(struct Cookie *co, if(!co->name) return CURLE_OUT_OF_MEMORY; else { - /* For Netscape file format cookies we check prefix on the name */ - if(curl_strnequal("__Secure-", co->name, 9)) + /* For Netscape file format cookies we check prefix on the name. + These prefixes are matched case sensitively, same as on the + header path and as the 6265bis document specifies. */ + if(!strncmp("__Secure-", co->name, 9)) co->prefix_secure = TRUE; - else if(curl_strnequal("__Host-", co->name, 7)) + else if(!strncmp("__Host-", co->name, 7)) co->prefix_host = TRUE; } break; @@ -765,12 +788,20 @@ static CURLcode parse_netscape(struct Cookie *co, /* we did not find the sufficient number of fields */ return CURLE_OK; + /* Reject control octets in the name or value, matching the filtering done + for cookies set over HTTP. A cookie loaded from a file is later sent in + request headers, so the same bytes that make a server reject a request + must not slip in through the file. */ + if(invalid_octets(co->name, strlen(co->name)) || + invalid_octets(co->value, strlen(co->value))) + return CURLE_OK; + *okay = TRUE; return CURLE_OK; } static bool is_public_suffix(struct Curl_easy *data, - struct Cookie *co, + const struct Cookie *co, const char *domain) { #ifdef USE_LIBPSL @@ -787,12 +818,21 @@ static bool is_public_suffix(struct Curl_easy *data, char lcookie[256]; size_t dlen = strlen(domain); size_t clen = strlen(co->domain); + + /* trim trailing dots */ + if(dlen && (domain[dlen - 1] == '.')) + dlen--; + if(clen && (co->domain[clen - 1] == '.')) + clen--; + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { const psl_ctx_t *psl = Curl_psl_use(data); if(psl) { /* the PSL check requires lowercase domain name and pattern */ - Curl_strntolower(lcase, domain, dlen + 1); - Curl_strntolower(lcookie, co->domain, clen + 1); + Curl_strntolower(lcase, domain, dlen); + lcase[dlen] = 0; + Curl_strntolower(lcookie, co->domain, clen); + lcookie[clen] = 0; acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); Curl_psl_release(data); } @@ -819,7 +859,7 @@ static bool is_public_suffix(struct Curl_easy *data, /* returns TRUE when replaced */ static bool replace_existing(struct Curl_easy *data, struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool secure, bool *replacep) { @@ -834,8 +874,10 @@ static bool replace_existing(struct Curl_easy *data, bool matching_domains = FALSE; if(clist->domain && co->domain) { - if(curl_strequal(clist->domain, co->domain)) - /* The domains are identical */ + if(cookie_tailmatch(clist->domain, strlen(clist->domain), + co->domain) || + cookie_tailmatch(co->domain, strlen(co->domain), clist->domain)) + /* The existing one is a tail of the new or vice versa */ matching_domains = TRUE; } else if(!clist->domain && !co->domain) @@ -862,7 +904,7 @@ static bool replace_existing(struct Curl_easy *data, else cllen = strlen(clist->path); - if(curl_strnequal(clist->path, co->path, cllen)) { + if(!strncmp(clist->path, co->path, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); return FALSE; @@ -886,7 +928,7 @@ static bool replace_existing(struct Curl_easy *data, /* the domains were identical */ if(clist->path && co->path && - !curl_strequal(clist->path, co->path)) + strcmp(clist->path, co->path)) replace_old = FALSE; else if(!clist->path != !co->path) replace_old = FALSE; @@ -1152,7 +1194,7 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, curlx_fclose(handle); } data->state.cookie_engine = TRUE; - ci->running = TRUE; /* now, we are running */ + ci->running = TRUE; /* now, we are running */ return result; } @@ -1237,9 +1279,9 @@ static int cookie_sort_ct(const void *p1, const void *p2) return (c2->creationtime > c1->creationtime) ? 1 : -1; } -bool Curl_secure_context(struct connectdata *conn, const char *host) +bool Curl_secure_context(struct Curl_easy *data, const char *host) { - return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || + return Curl_xfer_is_secure(data) || curl_strequal("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "::1"); @@ -1249,15 +1291,13 @@ bool Curl_secure_context(struct connectdata *conn, const char *host) * Curl_cookie_getlist * * For a given host and path, return a linked list of cookies that the client - * should send to the server if used now. The secure boolean informs the cookie - * if a secure connection is achieved or not. + * should send to the server if used now. * * It shall only return cookies that have not expired. * * 'okay' is TRUE when there is a list returned. */ CURLcode Curl_cookie_getlist(struct Curl_easy *data, - struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) @@ -1266,7 +1306,7 @@ CURLcode Curl_cookie_getlist(struct Curl_easy *data, const bool is_ip = Curl_host_is_ipnum(host); const size_t myhash = cookiehash(host); struct Curl_llist_node *n; - const bool secure = Curl_secure_context(conn, host); + const bool secure = Curl_secure_context(data, host); struct CookieInfo *ci = data->cookies; const char *path = data->state.up.path; CURLcode result = CURLE_OK; @@ -1561,7 +1601,7 @@ static CURLcode cookie_output(struct Curl_easy *data, return result; } -static struct curl_slist *cookie_list(struct Curl_easy *data) +static struct curl_slist *cookie_list(const struct Curl_easy *data) { struct curl_slist *list = NULL; struct curl_slist *beg; diff --git a/deps/curl/lib/cookie.h b/deps/curl/lib/cookie.h index f66e0ef591..b8ef8b8ea1 100644 --- a/deps/curl/lib/cookie.h +++ b/deps/curl/lib/cookie.h @@ -109,7 +109,7 @@ struct connectdata; * are only used if the header boolean is TRUE. */ -bool Curl_secure_context(struct connectdata *conn, const char *host); +bool Curl_secure_context(struct Curl_easy *data, const char *host); CURLcode Curl_cookie_add(struct Curl_easy *data, struct CookieInfo *ci, bool httpheader, @@ -118,7 +118,7 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, const char *domain, const char *path, bool secure) WARN_UNUSED_RESULT; -CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, +CURLcode Curl_cookie_getlist(struct Curl_easy *data, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; void Curl_cookie_clearall(struct CookieInfo *ci); diff --git a/deps/curl/lib/creds.c b/deps/curl/lib/creds.c new file mode 100644 index 0000000000..d22c166a42 --- /dev/null +++ b/deps/curl/lib/creds.c @@ -0,0 +1,189 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include /* for offsetof() */ + +#include "creds.h" +#include "curl_trc.h" +#include "strcase.h" +#include "urldata.h" + + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, + uint8_t source, + struct Curl_creds **pcreds) +{ + struct Curl_creds *creds = NULL; + size_t ulen = user ? strlen(user) : 0; + size_t plen = passwd ? strlen(passwd) : 0; + size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; + size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; + size_t sslen = sasl_service ? strlen(sasl_service) : 0; + char *s, *buf; + CURLcode result = CURLE_OK; + + Curl_creds_unlink(pcreds); + + /* Everything empty/NULL, this is the NULL credential */ + if(!user && !passwd && !olen && !salen && !sslen) + goto out; + + if((ulen > CURL_MAX_INPUT_LENGTH) || + (plen > CURL_MAX_INPUT_LENGTH) || + (olen > CURL_MAX_INPUT_LENGTH) || + (salen > CURL_MAX_INPUT_LENGTH) || + (sslen > CURL_MAX_INPUT_LENGTH)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* null-terminator for user already part of struct */ + creds = curlx_calloc(1, sizeof(*creds) + + ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1); + if(!creds) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + creds->refcount = 1; + creds->source = source; + /* Some compilers try to be too smart about our dynamic struct size */ + buf = ((char *)creds) + offsetof(struct Curl_creds, buf); + creds->user = s = buf; + if(ulen) + memcpy(s, user, ulen + 1); + creds->passwd = s = buf + ulen + 1; + if(plen) + memcpy(s, passwd, plen + 1); + creds->oauth_bearer = s = buf + ulen + 1 + plen + 1; + if(olen) + memcpy(s, oauth_bearer, olen + 1); + creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1; + if(salen) + memcpy(s, sasl_authzid, salen + 1); + creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1; + if(sslen) + memcpy(s, sasl_service, sslen + 1); + +out: + if(!result) + *pcreds = creds; + else + Curl_creds_unlink(&creds); + return result; +} + +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out) +{ + struct Curl_creds *creds_out = NULL; + CURLcode result; + + if(!creds_in) { + result = Curl_creds_create(user, passwd, NULL, NULL, NULL, + source, &creds_out); + } + else { + result = Curl_creds_create(user ? user : Curl_creds_user(creds_in), + passwd ? passwd : Curl_creds_passwd(creds_in), + Curl_creds_oauth_bearer(creds_in), + Curl_creds_sasl_authzid(creds_in), + Curl_creds_sasl_service(creds_in), + source, &creds_out); + } + Curl_creds_link(pcreds_out, creds_out); + Curl_creds_unlink(&creds_out); + return result; +} + +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src) +{ + if(*pdest != src) { + Curl_creds_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_creds_unlink(struct Curl_creds **pcreds) +{ + if(*pcreds) { + struct Curl_creds *creds = *pcreds; + + DEBUGASSERT(creds->refcount); + *pcreds = NULL; + if(creds->refcount) + creds->refcount--; + if(!creds->refcount) { + curlx_free(creds); + } + } +} + +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return (c1 == c2) || + (c1 && c2 && + !Curl_timestrcmp(c1->user, c2->user) && + !Curl_timestrcmp(c1->passwd, c2->passwd) && + !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer) && + !Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) && + !Curl_timestrcmp(c1->sasl_service, c2->sasl_service)); +} + +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return Curl_creds_same(c1, c2) && + ((c1 == c2) || (c1 && c2 && (c1->source == c2->source))); +} + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg) +{ + if(creds) { + CURL_TRC_M(data, "%s: user=%s, passwd=%s, " + "sasl_authzid=%s, oauth_bearer=%s, source=%d", + msg, + Curl_creds_user(creds), + Curl_creds_has_passwd(creds) ? "***" : "", + Curl_creds_sasl_authzid(creds), + Curl_creds_has_oauth_bearer(creds) ? "***" : "", + creds->source); + } + else + CURL_TRC_M(data, "%s: -", msg); +} +#endif diff --git a/deps/curl/lib/creds.h b/deps/curl/lib/creds.h new file mode 100644 index 0000000000..0664d08a4f --- /dev/null +++ b/deps/curl/lib/creds.h @@ -0,0 +1,91 @@ +#ifndef HEADER_CURL_CREDS_H +#define HEADER_CURL_CREDS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; + +#define CREDS_NONE 0 /* used for default username/passwd */ +#define CREDS_URL 1 /* username/passwd from URL */ +#define CREDS_OPTION 2 /* username/passwd set with a CURLOPT_ */ +#define CREDS_NETRC 3 /* username/passwd found in netrc */ + +struct Curl_creds { + const char *user; /* non-NULL, maybe empty string */ + const char *passwd; /* non-NULL, maybe empty string */ + const char *oauth_bearer; /* non-NULL, maybe empty string */ + const char *sasl_authzid; /* non-NULL, maybe empty string */ + const char *sasl_service; /* non-NULL, maybe empty string */ + uint32_t refcount; + uint8_t source; /* CREDS_* value */ + char buf[1]; +}; + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, + uint8_t source, + struct Curl_creds **pcreds); + +/* Create credentials by overriding `user` and/or `passwd` in `creds_in` */ +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out); + +/* Unlink any creds in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src); + +/* Drop a reference, creds may be passed as NULL */ +void Curl_creds_unlink(struct Curl_creds **pcreds); + +/* TRUE if both creds are NULL or have same values, except source. */ +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); + +/* TRUE if both creds are NULL or have all values equal. */ +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2); + +/* Provides properties for creds or, if creds is NULL, the empty string */ +#define Curl_creds_has_user(c) ((c) && (c)->user[0]) +#define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) +#define Curl_creds_has_user_or_pass(c) \ + ((c) && ((c)->user[0] || (c)->passwd[0])) +#define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) +#define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) +#define Curl_creds_user(c) ((c) ? (c)->user : "") +#define Curl_creds_passwd(c) ((c) ? (c)->passwd : "") +#define Curl_creds_oauth_bearer(c) ((c) ? (c)->oauth_bearer : "") +#define Curl_creds_sasl_authzid(c) ((c) ? (c)->sasl_authzid : "") +#define Curl_creds_sasl_service(c) ((c) ? (c)->sasl_service : "") + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg); +#endif + +#endif /* HEADER_CURL_CREDS_H */ diff --git a/deps/curl/lib/cshutdn.c b/deps/curl/lib/cshutdn.c index 27b4a9f0dd..cc77e401fd 100644 --- a/deps/curl/lib/cshutdn.c +++ b/deps/curl/lib/cshutdn.c @@ -150,8 +150,8 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T, conn->bits.shutdown_filters ? "" : "force ", conn->connection_id); - Curl_conn_close(admin, SECONDARYSOCKET); - Curl_conn_close(admin, FIRSTSOCKET); + Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET); + Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); Curl_detach_connection(admin); if(data->multi) @@ -322,14 +322,14 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, DEBUGASSERT(multi); cshutdn->multi = multi; Curl_llist_init(&cshutdn->list, NULL); - cshutdn->initialised = TRUE; + cshutdn->initialized = TRUE; return 0; /* good */ } void Curl_cshutdn_destroy(struct cshutdn *cshutdn, struct Curl_easy *data) { - if(cshutdn->initialised && data) { + if(cshutdn->initialized && data) { int timeout_ms = 0; /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD diff --git a/deps/curl/lib/cshutdn.h b/deps/curl/lib/cshutdn.h index b2e83f3d1a..8479524993 100644 --- a/deps/curl/lib/cshutdn.h +++ b/deps/curl/lib/cshutdn.h @@ -54,7 +54,7 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, struct cshutdn { struct Curl_llist list; /* connections being shut down */ struct Curl_multi *multi; /* the multi owning this */ - BIT(initialised); + BIT(initialized); }; /* Init as part of the given multi handle. */ diff --git a/deps/curl/lib/curl_addrinfo.c b/deps/curl/lib/curl_addrinfo.c index fd26c5f0bc..a927d44c99 100644 --- a/deps/curl/lib/curl_addrinfo.c +++ b/deps/curl/lib/curl_addrinfo.c @@ -49,6 +49,7 @@ #include "curl_addrinfo.h" #include "fake_addrinfo.h" #include "curlx/inet_pton.h" +#include "curlx/strparse.h" /* * Curl_freeaddrinfo() @@ -59,7 +60,7 @@ * any function call which actually allocates a Curl_addrinfo struct. */ -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ +#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) /* workaround icc 9.1 optimizer issue */ # define vqualifier volatile @@ -113,7 +114,7 @@ int Curl_getaddrinfo_ex(const char *nodename, /* traverse the addrinfo list */ - for(ai = aihead; ai != NULL; ai = ai->ai_next) { + for(ai = aihead; ai; ai = ai->ai_next) { size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; /* ignore elements with unsupported address family, settle family-specific sockaddr structure size. */ @@ -256,7 +257,7 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) /* no input == no output! */ return NULL; - DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); + DEBUGASSERT(he->h_name && he->h_addr_list); for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; @@ -443,6 +444,48 @@ bool Curl_is_ipaddr(const char *address) return FALSE; } +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone) +{ + const char *zonep = NULL; + size_t i = 0, hlen = 0, zlen = 0; + + if(host) + memset(host, 0, sizeof(*host)); + if(zone) + memset(zone, 0, sizeof(*zone)); + + for(i = 0; i < len; ++i, ++hlen) { + if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) + break; + } + + if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ + i += 1; + if(maybe_url_encoded && !strncmp("25", s + i, 2)) + i += 2; + zonep = s + i; + for(; i < len; ++i, ++zlen) { + /* Allow unreserved characters as defined in RFC 3986 */ + if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || + (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) + break; + } + } + + if(i != len) + return FALSE; /* invalid chars in zone */ + if(host && hlen) { + host->str = s; + host->len = hlen; + } + if(zone && zlen) { + zone->str = zonep; + zone->len = zlen; + } + return TRUE; +} + #ifdef USE_UNIX_SOCKETS /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo @@ -560,7 +603,7 @@ int curl_dbg_getaddrinfo(const char *hostname, #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /* - * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS + * Works around the sin6_port is always zero bug on iOS 9.3.2 and macOS * 10.11.5. */ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) @@ -570,7 +613,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) #ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif - for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { + for(ca = addrinfo; ca; ca = ca->ai_next) { switch(ca->ai_family) { case AF_INET: addr = (void *)ca->ai_addr; /* storage area for this info */ diff --git a/deps/curl/lib/curl_addrinfo.h b/deps/curl/lib/curl_addrinfo.h index da2da872cf..046be23816 100644 --- a/deps/curl/lib/curl_addrinfo.h +++ b/deps/curl/lib/curl_addrinfo.h @@ -40,6 +40,8 @@ # include #endif +struct Curl_str; + /* * Curl_addrinfo is our internal struct definition that we use to allow * consistent internal handling of this data. We use this even when the system @@ -73,6 +75,9 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); bool Curl_is_ipv4addr(const char *address); bool Curl_is_ipaddr(const char *address); +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone); + CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp); diff --git a/deps/curl/lib/curl_config-cmake.h.in b/deps/curl/lib/curl_config-cmake.h.in index 41b0ddf073..c38caa2a03 100644 --- a/deps/curl/lib/curl_config-cmake.h.in +++ b/deps/curl/lib/curl_config-cmake.h.in @@ -22,13 +22,13 @@ * ***************************************************************************/ -/* Location of default ca bundle */ +/* Location of default CA bundle */ #cmakedefine CURL_CA_BUNDLE "${CURL_CA_BUNDLE}" -/* define "1" to use built-in ca store of TLS backend */ +/* define "1" to use built-in CA store of TLS backend */ #cmakedefine CURL_CA_FALLBACK 1 -/* Location of default ca path */ +/* Location of default CA path */ #cmakedefine CURL_CA_PATH "${CURL_CA_PATH}" /* Default SSL backend */ @@ -234,6 +234,12 @@ /* Define to 1 if you have the `opendir' function. */ #cmakedefine HAVE_OPENDIR 1 +/* Define to 1 if you have the memset_explicit (C23) function. */ +#cmakedefine HAVE_MEMSET_EXPLICIT 1 + +/* Define to 1 if you have the memset_s (C11) function. */ +#cmakedefine HAVE_MEMSET_S 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 @@ -255,7 +261,7 @@ /* Define to 1 if you have a working getaddrinfo function. */ #cmakedefine HAVE_GETADDRINFO 1 -/* Define to 1 if the getaddrinfo function is threadsafe. */ +/* Define to 1 if the getaddrinfo function is thread-safe. */ #cmakedefine HAVE_GETADDRINFO_THREADSAFE 1 /* Define to 1 if you have the `geteuid' function. */ @@ -706,6 +712,9 @@ ${SIZEOF_TIME_T_CODE} /* if libuv is in use */ #cmakedefine USE_LIBUV 1 +/* if HTTP/3 proxy support is available */ +#cmakedefine USE_PROXY_HTTP3 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UV_H 1 diff --git a/deps/curl/lib/curl_config.h.in b/deps/curl/lib/curl_config.h.in index ec9b71febd..405ba1aa6c 100644 --- a/deps/curl/lib/curl_config.h.in +++ b/deps/curl/lib/curl_config.h.in @@ -2,7 +2,7 @@ /* !checksrc! disable COPYRIGHT all */ -/* Location of default ca bundle */ +/* Location of default CA bundle */ #undef CURL_CA_BUNDLE /* define "1" to use OpenSSL's built-in CA store */ @@ -11,7 +11,7 @@ /* If native CA store is enabled */ #undef CURL_CA_NATIVE -/* Location of default ca path */ +/* Location of default CA path */ #undef CURL_CA_PATH /* If safe CA bundle search is enabled */ @@ -110,7 +110,7 @@ /* disable netrc parsing */ #undef CURL_DISABLE_NETRC -/* if the OpenSSL configuration will not be loaded automatically */ +/* if the OpenSSL configuration is not loaded automatically */ #undef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG /* disable date parsing */ @@ -439,6 +439,12 @@ /* Define to 1 if you have the memrchr function or macro. */ #undef HAVE_MEMRCHR +/* Define to 1 if you have the `memset_explicit' function. */ +#undef HAVE_MEMSET_EXPLICIT + +/* Define to 1 if you have the memset_s function. */ +#undef HAVE_MEMSET_S + /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H @@ -849,6 +855,9 @@ /* if OpenSSL is in use */ #undef USE_OPENSSL +/* if HTTP/3 proxy support is available */ +#undef USE_PROXY_HTTP3 + /* if quiche is in use */ #undef USE_QUICHE diff --git a/deps/curl/lib/curl_endian.c b/deps/curl/lib/curl_endian.c index 864b411b68..b3689417a4 100644 --- a/deps/curl/lib/curl_endian.c +++ b/deps/curl/lib/curl_endian.c @@ -34,7 +34,7 @@ * * Parameters: * - * buf [in] - A pointer to a 2 byte buffer. + * buf [in] - A pointer to a 2-byte buffer. * * Returns the integer. */ @@ -53,7 +53,7 @@ unsigned short Curl_read16_le(const unsigned char *buf) * * Parameters: * - * buf [in] - A pointer to a 4 byte buffer. + * buf [in] - A pointer to a 4-byte buffer. * * Returns the integer. */ @@ -72,7 +72,7 @@ unsigned int Curl_read32_le(const unsigned char *buf) * * Parameters: * - * buf [in] - A pointer to a 2 byte buffer. + * buf [in] - A pointer to a 2-byte buffer. * * Returns the integer. */ diff --git a/deps/curl/lib/curl_fnmatch.c b/deps/curl/lib/curl_fnmatch.c index dde956c3ac..d165554f8d 100644 --- a/deps/curl/lib/curl_fnmatch.c +++ b/deps/curl/lib/curl_fnmatch.c @@ -96,25 +96,25 @@ static int parsekeyword(const unsigned char **pattern, unsigned char *charset) #undef KEYLEN *pattern = p; /* move caller's pattern pointer */ - if(strcmp(keyword, "digit") == 0) + if(!strcmp(keyword, "digit")) charset[CURLFNM_DIGIT] = 1; - else if(strcmp(keyword, "alnum") == 0) + else if(!strcmp(keyword, "alnum")) charset[CURLFNM_ALNUM] = 1; - else if(strcmp(keyword, "alpha") == 0) + else if(!strcmp(keyword, "alpha")) charset[CURLFNM_ALPHA] = 1; - else if(strcmp(keyword, "xdigit") == 0) + else if(!strcmp(keyword, "xdigit")) charset[CURLFNM_XDIGIT] = 1; - else if(strcmp(keyword, "print") == 0) + else if(!strcmp(keyword, "print")) charset[CURLFNM_PRINT] = 1; - else if(strcmp(keyword, "graph") == 0) + else if(!strcmp(keyword, "graph")) charset[CURLFNM_GRAPH] = 1; - else if(strcmp(keyword, "space") == 0) + else if(!strcmp(keyword, "space")) charset[CURLFNM_SPACE] = 1; - else if(strcmp(keyword, "blank") == 0) + else if(!strcmp(keyword, "blank")) charset[CURLFNM_BLANK] = 1; - else if(strcmp(keyword, "upper") == 0) + else if(!strcmp(keyword, "upper")) charset[CURLFNM_UPPER] = 1; - else if(strcmp(keyword, "lower") == 0) + else if(!strcmp(keyword, "lower")) charset[CURLFNM_LOWER] = 1; else return SETCHARSET_FAIL; diff --git a/deps/curl/lib/curl_fopen.c b/deps/curl/lib/curl_fopen.c index cc888f7616..abb17eab4b 100644 --- a/deps/curl/lib/curl_fopen.c +++ b/deps/curl/lib/curl_fopen.c @@ -42,13 +42,13 @@ */ #ifdef _WIN32 -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) (((x) == '/') || ((x) == '\\')) #elif defined(MSDOS) || defined(OS2) -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) ((x) == '\\') #else -#define PATHSEP "/" +#define PATHSEP "/" #define IS_SEP(x) ((x) == '/') #endif diff --git a/deps/curl/lib/curl_gssapi.c b/deps/curl/lib/curl_gssapi.c index 650d1908d0..07a6c1e7ed 100644 --- a/deps/curl/lib/curl_gssapi.c +++ b/deps/curl/lib/curl_gssapi.c @@ -260,7 +260,7 @@ static OM_uint32 stub_gss_init_sec_context( used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds, (int)target_desc.length, (const char *)target_desc.value, - ctx->sent); + (int)ctx->sent); gss_release_buffer(&minor_status, &target_desc); } @@ -302,8 +302,7 @@ static OM_uint32 stub_gss_delete_sec_context( return GSS_S_FAILURE; } - curlx_free(*context); - *context = NULL; + curlx_safefree(*context); *min = 0; return GSS_S_COMPLETE; diff --git a/deps/curl/lib/curl_hmac.h b/deps/curl/lib/curl_hmac.h index 301d44fee8..d9a697a0a5 100644 --- a/deps/curl/lib/curl_hmac.h +++ b/deps/curl/lib/curl_hmac.h @@ -24,26 +24,26 @@ * ***************************************************************************/ -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ - !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) #define HMAC_MD5_LENGTH 16 typedef CURLcode (*HMAC_hinit)(void *context); -typedef void (*HMAC_hupdate)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (*HMAC_hfinal)(unsigned char *result, void *context); +typedef void (*HMAC_hupdate)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (*HMAC_hfinal)(unsigned char *result, void *context); /* Per-hash function HMAC parameters. */ struct HMAC_params { - HMAC_hinit hinit; /* Initialize context procedure. */ - HMAC_hupdate hupdate; /* Update context with data. */ - HMAC_hfinal hfinal; /* Get final result procedure. */ - unsigned int ctxtsize; /* Context structure size. */ - unsigned int maxkeylen; /* Maximum key length (bytes). */ - unsigned int resultlen; /* Result length (bytes). */ + HMAC_hinit hinit; /* Initialize context procedure. */ + HMAC_hupdate hupdate; /* Update context with data. */ + HMAC_hfinal hfinal; /* Get final result procedure. */ + unsigned int ctxtsize; /* Context structure size. */ + unsigned int maxkeylen; /* Maximum key length (bytes). */ + unsigned int resultlen; /* Result length (bytes). */ }; /* HMAC computation context. */ @@ -66,7 +66,6 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, const unsigned char *data, size_t datalen, unsigned char *output); - #endif #endif /* HEADER_CURL_HMAC_H */ diff --git a/deps/curl/lib/curl_md5.h b/deps/curl/lib/curl_md5.h index 8a0cc2623e..1beaed5e66 100644 --- a/deps/curl/lib/curl_md5.h +++ b/deps/curl/lib/curl_md5.h @@ -38,16 +38,16 @@ typedef void (*Curl_MD5_update_func)(void *context, typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context); struct MD5_params { - Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ - Curl_MD5_update_func md5_update_func; /* Update context with data */ - Curl_MD5_final_func md5_final_func; /* Get final result procedure */ - unsigned int md5_ctxtsize; /* Context structure size */ - unsigned int md5_resultlen; /* Result length (bytes) */ + Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ + Curl_MD5_update_func md5_update_func; /* Update context with data */ + Curl_MD5_final_func md5_final_func; /* Get final result procedure */ + unsigned int md5_ctxtsize; /* Context structure size */ + unsigned int md5_resultlen; /* Result length (bytes) */ }; struct MD5_context { - const struct MD5_params *md5_hash; /* Hash function definition */ - void *md5_hashctx; /* Hash function context */ + const struct MD5_params *md5_hash; /* Hash function definition */ + void *md5_hashctx; /* Hash function context */ }; extern const struct MD5_params Curl_DIGEST_MD5; diff --git a/deps/curl/lib/curl_ntlm_core.c b/deps/curl/lib/curl_ntlm_core.c index 4b2007bbad..58a446c95d 100644 --- a/deps/curl/lib/curl_ntlm_core.c +++ b/deps/curl/lib/curl_ntlm_core.c @@ -49,6 +49,13 @@ in NTLM type-3 messages. */ +#ifdef USE_GNUTLS +#include +#if NETTLE_VERSION_MAJOR < 4 +#define USE_GNUTLS_DES +#endif +#endif + #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) # include @@ -63,7 +70,7 @@ # include # define USE_WOLFSSL_DES -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) # include # define USE_CURL_DES_SET_ODD_PARITY #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) @@ -175,7 +182,7 @@ static void setup_des_key(const unsigned char *key_56, Des *des) wc_Des_SetKey(des, key, NULL, 0); } -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -244,6 +251,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, char key[8]; } blob; DWORD len = 8; + BOOL res; /* Acquire the crypto provider */ if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, @@ -273,19 +281,19 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, memcpy(out, in, 8); /* Perform the encryption */ - CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); + res = CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); CryptDestroyKey(hkey); CryptReleaseContext(hprov, 0); - return TRUE; + return res; } #endif /* crypto backends */ /* - * takes a 21 byte array and treats it as 3 56-bit DES keys. The - * 8 byte plaintext is encrypted with each key and the resulting 24 + * takes a 21-byte array and treats it as 3 56-bit DES keys. The + * 8-byte plaintext is encrypted with each key and the resulting 24 * bytes are stored in the results array. */ void Curl_ntlm_core_lm_resp(const unsigned char *keys, @@ -314,7 +322,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE); setup_des_key(keys + 14, &des); wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE); -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(keys, &des); des_encrypt(&des, 8, results, plaintext); @@ -367,7 +375,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE); setup_des_key(pw + 7, &des); wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE); -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(pw, &des); des_encrypt(&des, 8, lmbuffer, magic); @@ -625,7 +633,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, * * ntlmv2hash [in] - The NTLMv2 hash (16 bytes) * challenge_client [in] - The client nonce (8 bytes) - * challenge_client [in] - The server challenge (8 bytes) + * challenge_server [in] - The server challenge (8 bytes) * lmresp [out] - The LMv2 response (24 bytes) * * Returns CURLE_OK on success. diff --git a/deps/curl/lib/curl_ntlm_core.h b/deps/curl/lib/curl_ntlm_core.h index f96bf0ada5..df24158f8e 100644 --- a/deps/curl/lib/curl_ntlm_core.h +++ b/deps/curl/lib/curl_ntlm_core.h @@ -30,7 +30,7 @@ struct ntlmdata; /* Helpers to generate function byte arguments in little endian order */ -#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) +#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ ((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff)) diff --git a/deps/curl/lib/curl_sasl.c b/deps/curl/lib/curl_sasl.c index 60f085901f..7e867d753f 100644 --- a/deps/curl/lib/curl_sasl.c +++ b/deps/curl/lib/curl_sasl.c @@ -35,7 +35,7 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ + !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) #include "urldata.h" @@ -276,7 +276,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) { /* Have credentials been provided? */ - if(data->conn->user[0]) + if(data->conn->creds) return TRUE; /* EXTERNAL can authenticate without a username and/or password */ @@ -299,13 +299,15 @@ struct sasl_ctx { static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) { - if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { + if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && + !Curl_creds_has_passwd(sctx->conn->creds)) { sctx->mech = SASL_MECH_STRING_EXTERNAL; sctx->state1 = SASL_EXTERNAL; sctx->sasl->authused = SASL_MECH_EXTERNAL; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_external_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_external_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -316,10 +318,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) { if((sctx->enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && - Curl_auth_user_contains_domain(sctx->conn->user)) { - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sctx->sasl->params->service; + Curl_auth_user_contains_domain(sctx->conn->creds)) { + const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; sctx->sasl->mutual_auth = FALSE; sctx->mech = SASL_MECH_STRING_GSSAPI; @@ -330,9 +331,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn); sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, sctx->conn->user, - sctx->conn->passwd, - service, sctx->conn->host.name, + Curl_auth_create_gssapi_user_message(data, sctx->conn->creds, + service, + sctx->conn->origin->hostname, (bool)sctx->sasl->mutual_auth, NULL, krb5, &sctx->resp); } @@ -374,8 +375,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) Curl_bufref_init(&nullmsg); sctx->state1 = SASL_GSASL; sctx->state2 = SASL_GSASL; - sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user, - sctx->conn->passwd, gsasl); + sctx->result = Curl_auth_gsasl_start(data, sctx->conn->creds, gsasl); if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); return TRUE; @@ -411,9 +411,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) { if((sctx->enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sctx->sasl->params->service; + const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; const char *hostname; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); @@ -426,9 +425,7 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE); sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_ntlm_type1_message(data, sctx->conn->creds, service, hostname, ntlm, &sctx->resp); } @@ -440,11 +437,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + if(Curl_creds_has_oauth_bearer(data->state.creds) && + (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { const char *hostname; int port; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -456,9 +450,8 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_oauth_bearer_message(sctx->conn->user, - hostname, port, - oauth_bearer, &sctx->resp); + Curl_auth_create_oauth_bearer_message(sctx->conn->creds, + hostname, port, &sctx->resp); return TRUE; } return FALSE; @@ -466,19 +459,15 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = - (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { + if(Curl_creds_has_oauth_bearer(sctx->conn->creds) && + (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { sctx->mech = SASL_MECH_STRING_XOAUTH2; sctx->state1 = SASL_OAUTH2; sctx->sasl->authused = SASL_MECH_XOAUTH2; if(sctx->sasl->force_ir || data->set.sasl_ir) - sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user, - oauth_bearer, - &sctx->resp); + sctx->result = Curl_auth_create_xoauth_bearer_message( + sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -493,9 +482,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_plain_message(sctx->conn->sasl_authzid, - sctx->conn->user, sctx->conn->passwd, - &sctx->resp); + Curl_auth_create_plain_message(sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -510,7 +497,8 @@ static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) sctx->sasl->authused = SASL_MECH_LOGIN; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_login_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_login_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -599,13 +587,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; const char *hostname; int port; -#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \ - !defined(CURL_DISABLE_DIGEST_AUTH) - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; -#endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -633,18 +614,17 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, &resp); + result = Curl_auth_create_plain_message(conn->creds, &resp); break; case SASL_LOGIN: - Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(Curl_creds_user(conn->creds), &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - Curl_auth_create_login_message(conn->passwd, &resp); + Curl_auth_create_login_message(Curl_creds_passwd(conn->creds), &resp); break; case SASL_EXTERNAL: - Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(Curl_creds_user(conn->creds), &resp); break; #ifdef USE_GSASL case SASL_GSASL: @@ -662,15 +642,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, - conn->passwd, &resp); + result = Curl_auth_create_cram_md5_message(&serverdata, conn->creds, + &resp); break; case SASL_DIGESTMD5: result = get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_create_digest_md5_message(data, &serverdata, - conn->user, conn->passwd, - service, &resp); + conn->creds, + sasl->params->service, + &resp); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) newstate = SASL_DIGESTMD5_RESP; break; @@ -684,9 +665,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, /* Create the type-1 message */ struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, hostname, + Curl_auth_create_ntlm_type1_message(data, conn->creds, + sasl->params->service, hostname, ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; break; @@ -699,9 +679,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!result) result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); if(!result) - result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, ntlm, - &resp); + result = Curl_auth_create_ntlm_type3_message(data, conn->creds, + ntlm, &resp); break; } #endif @@ -710,8 +689,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_GSSAPI: { struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, - service, conn->host.name, + Curl_auth_create_gssapi_user_message(data, conn->creds, + sasl->params->service, + conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); newstate = SASL_GSSAPI_TOKEN; @@ -726,7 +706,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, else if(sasl->mutual_auth) { /* Decode the user token challenge and create the optional response message */ - result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, + result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, (bool)sasl->mutual_auth, &serverdata, @@ -735,10 +715,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else /* Decode the security challenge and create the response message */ - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; case SASL_GSSAPI_NO_DATA: @@ -749,10 +728,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!krb5) result = CURLE_OUT_OF_MEMORY; else - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; #endif @@ -760,18 +738,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_OAUTH2: /* Create the authorization message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { - result = Curl_auth_create_oauth_bearer_message(conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->creds, hostname, port, - oauth_bearer, &resp); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ newstate = SASL_OAUTH2_RESP; } else - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, + result = Curl_auth_create_xoauth_bearer_message(conn->creds, &resp); break; @@ -861,7 +837,7 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, else { if(param_missing) infof(data, "SASL: %s is missing %s", mname, param_missing); - if(!data->conn->user[0]) + if(!Curl_creds_has_user(data->conn->creds)) infof(data, "SASL: %s is missing username", mname); } } @@ -903,7 +879,8 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) "auth mechanisms"); else { infof(data, "SASL: no auth mechanism offered could be selected"); - if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0]) + if((enabledmechs & SASL_MECH_EXTERNAL) && + Curl_creds_has_passwd(data->conn->creds)) infof(data, "SASL: auth EXTERNAL not chosen with password"); sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs, CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL); @@ -918,10 +895,10 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs, CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL); sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); } #endif /* CURLVERBOSE */ diff --git a/deps/curl/lib/curl_setup.h b/deps/curl/lib/curl_setup.h index 0d4a8fa577..861e07649d 100644 --- a/deps/curl/lib/curl_setup.h +++ b/deps/curl/lib/curl_setup.h @@ -219,7 +219,7 @@ /* please, do it beyond the point further indicated in this file. */ /* ================================================================ */ -/* Give calloc a chance to be dragging in early, so we do not redefine */ +/* Give calloc a chance to be included early, so we do not redefine */ #ifdef HAVE_THREADS_POSIX # include #endif @@ -461,7 +461,7 @@ # undef HAVE_FCNTL # undef HAVE_FCNTL_O_NONBLOCK # else - /* use libc networking and hence close() and fnctl() */ + /* use libc networking and hence close() and fcntl() */ # undef HAVE_CLOSESOCKET_CAMEL # undef HAVE_IOCTLSOCKET_CAMEL # endif @@ -587,7 +587,7 @@ # endif #endif -#if (SIZEOF_CURL_OFF_T < 8) +#if SIZEOF_CURL_OFF_T < 8 #error "too small curl_off_t" #else /* assume SIZEOF_CURL_OFF_T == 8 */ @@ -598,7 +598,7 @@ #define FMT_OFF_T CURL_FORMAT_CURL_OFF_T #define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU -#if (SIZEOF_TIME_T == 4) +#if SIZEOF_TIME_T == 4 # ifdef HAVE_TIME_T_UNSIGNED # define TIME_T_MAX UINT_MAX # define TIME_T_MIN 0 @@ -1137,6 +1137,15 @@ typedef unsigned int curl_bit; #define SOCKEWOULDBLOCK EWOULDBLOCK #endif +/* The socket error may be EWOULDBLOCK or on some systems EAGAIN when + it returned due to its inability to send/read data without blocking. + We treat both error codes the same here. */ +#if !defined(USE_WINSOCK) && EAGAIN != SOCKEWOULDBLOCK +#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK || (e) == EAGAIN) +#else +#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK) +#endif + /* * Macro argv_item_t hides platform details to code using it. */ @@ -1329,6 +1338,20 @@ extern curl_calloc_callback Curl_ccalloc; (ptr) = NULL; \ } while(0) +/* Same as curlx_safefree() but zeroes memory before freeing */ +#define curlx_safefreezero(ptr, size) \ + do { \ + curlx_freezero(ptr, size); \ + (ptr) = NULL; \ + } while(0) + +/* Same as curlx_safefreezero() but determines length with strlen() */ +#define curlx_safefreezeroz(ptr) \ + do { \ + curlx_freezeroz(ptr); \ + (ptr) = NULL; \ + } while(0) + #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ #ifdef DEBUGBUILD @@ -1542,7 +1565,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #endif #if defined(USE_UNIX_SOCKETS) && defined(_WIN32) -/* Offered by mingw-w64 v10+. MS SDK 10.17763/~VS2017+. */ +/* Offered by mingw-w64 v10+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR >= 10) # include #elif !defined(UNIX_PATH_MAX) /* Replicate logic present in afunix.h */ @@ -1578,7 +1601,7 @@ typedef struct sockaddr_un { /* The code is compiled with C++ compiler. C++ always supports 'inline'. */ # define CURL_INLINE inline /* 'inline' keyword supported */ -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 (and later) supports 'inline' keyword */ # define CURL_INLINE inline /* 'inline' keyword supported */ #elif defined(__GNUC__) && __GNUC__ >= 3 @@ -1608,4 +1631,43 @@ typedef struct sockaddr_un { #define NOVERBOSE(x) x #endif +/* For FreeBSD it is included from curl/curl.h */ +#if defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include /* for __DragonFly_version, OpenBSD, + __NetBSD_Version__ */ +#endif + +#ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */ +#ifdef _WIN32 +#if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ + (NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */ +#pragma comment(lib, "volatileaccessu.lib") +#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size) +#else +#define curlx_memzero(buf, size) SecureZeroMemory(buf, size) +#endif +#elif defined(HAVE_MEMSET_S) +#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size) +#elif defined(HAVE_MEMSET_EXPLICIT) +#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size) +#elif defined(__CYGWIN__) || \ + (defined(__NEWLIB__) && !defined(__CLIB2__)) || \ + (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \ + (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ + (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ + (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) +#define curlx_memzero(buf, size) explicit_bzero(buf, size) +#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ +#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size) +#endif +#endif /* !_CURL_LOCAL_MEMZERO */ + +#ifndef curlx_memzero +#define USE_CURLX_MEMZERO +void curlx_memzero(void *buf, size_t size); +#endif +void curlx_freezero(void *buf, size_t size); +void curlx_freezeroz(void *buf); + #endif /* HEADER_CURL_SETUP_H */ diff --git a/deps/curl/lib/curl_sha512_256.c b/deps/curl/lib/curl_sha512_256.c index 73b959c91d..c429bba8f2 100644 --- a/deps/curl/lib/curl_sha512_256.c +++ b/deps/curl/lib/curl_sha512_256.c @@ -44,7 +44,7 @@ # define USE_OPENSSL_SHA512_256 1 # define HAS_SHA512_256_IMPLEMENTATION 1 # ifdef __NetBSD__ -/* Some NetBSD versions has a bug in SHA-512/256. +/* Some NetBSD versions have a bug in SHA-512/256. * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 * The problematic versions: * - NetBSD before 9.4 @@ -54,7 +54,6 @@ * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as * the workaround reduces performance slightly. */ -# include # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ __NetBSD_Version__ < 1000000000) || \ @@ -75,7 +74,8 @@ #endif #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) -# include +# include +# include # ifdef SHA512_256_DIGEST_SIZE # define USE_GNUTLS_SHA512_256 1 # endif @@ -102,7 +102,7 @@ typedef EVP_MD_CTX *Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return CURLE_OK if succeed, @@ -145,7 +145,7 @@ static CURLcode Curl_sha512_256_update(void *context, Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; if(!EVP_DigestUpdate(*ctx, data, length)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } @@ -168,14 +168,14 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; - result = EVP_DigestFinal_ex(*ctx, - tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); - explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); + curlx_memzero(tmp_digest, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? - CURLE_OK : CURLE_SSL_CIPHER; + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); @@ -206,7 +206,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, do { word32 ilen = (word32)CURLMIN(length, UINT_MAX); if(wc_Sha512_256Update(ctx, data, ilen)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; length -= ilen; data += ilen; } while(length); @@ -216,7 +216,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) { if(wc_Sha512_256Final(ctx, digest)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } @@ -231,7 +231,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) typedef struct sha512_256_ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -262,7 +262,7 @@ static CURLcode Curl_sha512_256_update(void *context, { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); sha512_256_update(ctx, length, (const uint8_t *)data); @@ -281,8 +281,12 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; +#if NETTLE_VERSION_MAJOR >= 4 + sha512_256_digest(ctx, (uint8_t *)digest); +#else sha512_256_digest(ctx, (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest); +#endif return CURLE_OK; } @@ -296,7 +300,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) -# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 +# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901L # define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline)) # endif # endif @@ -430,7 +434,7 @@ struct Curl_sha512_256ctx { typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -456,7 +460,7 @@ static CURLcode Curl_sha512_256_init(void *context) ctx->H[6] = UINT64_C(0x2B0199FC2C85B8AA); ctx->H[7] = UINT64_C(0x0EB72DDC81C52CA2); - /* Initialise number of bytes and high part of number of bits. */ + /* Initialize number of bytes and high part of number of bits. */ ctx->count = UINT64_C(0); ctx->count_bits_hi = UINT64_C(0); @@ -494,14 +498,10 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ -#define SIG0(x) \ - (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) -#define SIG1(x) \ - (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) -#define sig0(x) \ - (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) -#define sig1(x) \ - (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) +#define SIG0(x) (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) +#define SIG1(x) (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) +#define sig0(x) (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) +#define sig1(x) (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) if(1) { unsigned int t; @@ -645,7 +645,7 @@ static CURLcode Curl_sha512_256_update(void *context, /* the void pointer here is required to mute Intel compiler warning */ void * const ctx_buf = ctx->buffer; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); if(length == 0) return CURLE_OK; /* Shortcut, do nothing */ @@ -711,7 +711,7 @@ static CURLcode Curl_sha512_256_update(void *context, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; - uint64_t num_bits; /* Number of processed bits */ + uint64_t num_bits; /* Number of processed bits */ unsigned int bytes_have; /* Number of bytes in the context buffer */ /* the void pointer here is required to mute Intel compiler warning */ void * const ctx_buf = ctx->buffer; diff --git a/deps/curl/lib/curl_share.c b/deps/curl/lib/curl_share.c index 386a2b547d..94469bc3ef 100644 --- a/deps/curl/lib/curl_share.c +++ b/deps/curl/lib/curl_share.c @@ -260,7 +260,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_CONNECT: /* It is safe to set this option several times on a share. */ - if(!share->cpool.initialised) { + if(!share->cpool.initialized) { Curl_cpool_init(&share->cpool, share->admin, share, 103); } break; diff --git a/deps/curl/lib/curl_share.h b/deps/curl/lib/curl_share.h index 69001be705..1d49a8d1dd 100644 --- a/deps/curl/lib/curl_share.h +++ b/deps/curl/lib/curl_share.h @@ -81,9 +81,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type); /* convenience macro to check if this handle is using a shared SSL spool */ -#define CURL_SHARE_ssl_scache(data) ((data)->share && \ - ((data)->share->specifier & \ - (1 << CURL_LOCK_DATA_SSL_SESSION))) +#define CURL_SHARE_ssl_scache(data) \ + ((data)->share && \ + ((data)->share->specifier & (1 << CURL_LOCK_DATA_SSL_SESSION))) CURLcode Curl_share_easy_unlink(struct Curl_easy *data); CURLcode Curl_share_easy_link(struct Curl_easy *data, diff --git a/deps/curl/lib/curl_sspi.h b/deps/curl/lib/curl_sspi.h index 3779d51753..2bd7eb4be8 100644 --- a/deps/curl/lib/curl_sspi.h +++ b/deps/curl/lib/curl_sspi.h @@ -52,25 +52,25 @@ extern PSecurityFunctionTable Curl_pSecFn; #define SP_NAME_NEGOTIATE "Negotiate" #define SP_NAME_KERBEROS "Kerberos" -/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #ifndef ISC_REQ_USE_HTTP_STYLE #define ISC_REQ_USE_HTTP_STYLE 0x01000000 #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_INVALID_PARAMETER #define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_DELEGATION_POLICY #define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_POLICY_NLTM_ONLY #define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_I_SIGNATURE_NEEDED #define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif diff --git a/deps/curl/lib/curl_trc.c b/deps/curl/lib/curl_trc.c index c6115cf7f6..f8287e420f 100644 --- a/deps/curl/lib/curl_trc.c +++ b/deps/curl/lib/curl_trc.c @@ -29,8 +29,9 @@ #include "multiif.h" #include "cf-dns.h" +#include "cf-recvbuf.h" #include "cf-socket.h" -#include "connect.h" +#include "cf-setup.h" #include "http2.h" #include "http_proxy.h" #include "cf-h1-proxy.h" @@ -563,6 +564,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_unix, TRC_CT_NETWORK }, { &Curl_cft_tcp_accept, TRC_CT_NETWORK }, { &Curl_cft_ip_happy, TRC_CT_NETWORK }, +#ifndef CURL_DISABLE_WEBSOCKETS + { &Curl_cft_recvbuf, TRC_CT_PROTOCOL }, +#endif { &Curl_cft_setup, TRC_CT_PROTOCOL }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) { &Curl_cft_nghttp2, TRC_CT_PROTOCOL }, @@ -578,6 +582,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_h1_proxy, TRC_CT_PROXY }, #ifdef USE_NGHTTP2 { &Curl_cft_h2_proxy, TRC_CT_PROXY }, +#endif +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) + { &Curl_cft_h3_proxy, TRC_CT_PROXY }, #endif { &Curl_cft_http_proxy, TRC_CT_PROXY }, #endif /* !CURL_DISABLE_HTTP */ diff --git a/deps/curl/lib/curlx/fopen.c b/deps/curl/lib/curlx/fopen.c index 6733010468..37ca02671a 100644 --- a/deps/curl/lib/curlx/fopen.c +++ b/deps/curl/lib/curlx/fopen.c @@ -80,7 +80,7 @@ static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8) } #endif -/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ +/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old Windows */ #if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \ (_WIN32_WINNT < _WIN32_WINNT_WIN10) WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *); @@ -247,7 +247,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out) CURLX_FREE(ibuf); CURLX_FREE(obuf); #endif - return *out ? true : false; + return !!*out; } #ifndef CURL_WINDOWS_UWP diff --git a/deps/curl/lib/curlx/strcopy.c b/deps/curl/lib/curlx/strcopy.c index 3e58ea24f1..3b80930a19 100644 --- a/deps/curl/lib/curlx/strcopy.c +++ b/deps/curl/lib/curlx/strcopy.c @@ -30,10 +30,10 @@ * * Provide the target buffer @dest and size of the target buffer @dsize, If * the source string @src with its *string length* @slen fits in the target - * buffer it is copied there - including storing a null terminator. + * buffer it is copied there - including storing a null-terminator. * * If the target buffer is too small, the copy is not performed but if the - * target buffer has a non-zero size it gets a null terminator stored. + * target buffer has a non-zero size it gets a null-terminator stored. */ void curlx_strcopy(char *dest, /* destination buffer */ size_t dsize, /* size of target buffer */ diff --git a/deps/curl/lib/curlx/strdup.c b/deps/curl/lib/curlx/strdup.c index 3c967dbe0a..8e788ea34c 100644 --- a/deps/curl/lib/curlx/strdup.c +++ b/deps/curl/lib/curlx/strdup.c @@ -94,3 +94,32 @@ void *curlx_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } + +#ifdef USE_CURLX_MEMZERO +static void *(* const volatile p_curlx_memset)(void *buf, int val, + size_t size) = memset; + +/* Local fallback in case there is no system function to securely zero a memory + buffer. */ +void curlx_memzero(void *buf, size_t size) +{ + if(buf) + p_curlx_memset(buf, 0, size); +} +#endif + +/* Free 'buf' after zeroing its content. */ +void curlx_freezero(void *buf, size_t size) +{ + if(buf) + curlx_memzero(buf, size); + curlx_free(buf); +} + +/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ +void curlx_freezeroz(void *buf) +{ + if(buf) + curlx_memzero(buf, strlen(buf)); + curlx_free(buf); +} diff --git a/deps/curl/lib/curlx/strerr.c b/deps/curl/lib/curlx/strerr.c index b53173c578..8e906cc6bd 100644 --- a/deps/curl/lib/curlx/strerr.c +++ b/deps/curl/lib/curlx/strerr.c @@ -190,7 +190,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len) p = "Winsock library is not ready"; break; case WSANOTINITIALISED: - p = "Winsock library not initialised"; + p = "Winsock library not initialized"; break; case WSAVERNOTSUPPORTED: p = "Winsock version not supported"; @@ -268,7 +268,7 @@ const char *curlx_strerror(int err, char *buf, size_t buflen) !get_winsock_error(err, buf, buflen) && #endif !curlx_get_winapi_error((DWORD)err, buf, buflen)) - SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err); + SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, (unsigned int)err); #else /* !_WIN32 */ #if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) diff --git a/deps/curl/lib/curlx/strparse.c b/deps/curl/lib/curlx/strparse.c index 7866b2087a..736f620504 100644 --- a/deps/curl/lib/curlx/strparse.c +++ b/deps/curl/lib/curlx/strparse.c @@ -45,13 +45,17 @@ void curlx_str_trim(struct Curl_str *out, size_t len) } /* Get a word until the first DELIM or end of string. At least one byte long. - return non-zero on error */ + return non-zero on error. If 'max' is zero, it will always return error. */ int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, char delim) { - const char *s = *linep; + const char *s; size_t len = 0; - DEBUGASSERT(linep && *linep && out && max && delim); + DEBUGASSERT(linep); + DEBUGASSERT(*linep); + DEBUGASSERT(out); + DEBUGASSERT(delim); + s = *linep; curlx_str_init(out); while(*s && (*s != delim)) { diff --git a/deps/curl/lib/cw-out.c b/deps/curl/lib/cw-out.c index 2af074e587..fad89b392c 100644 --- a/deps/curl/lib/cw-out.c +++ b/deps/curl/lib/cw-out.c @@ -195,7 +195,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, if(nwritten == CURL_WRITEFUNC_PAUSE) { if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is - actually only FILE:// now, and it cannot pause since the transfer is + actually only file:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; diff --git a/deps/curl/lib/cw-pause.c b/deps/curl/lib/cw-pause.c index ec611879d1..5561a8d7b7 100644 --- a/deps/curl/lib/cw-pause.c +++ b/deps/curl/lib/cw-pause.c @@ -117,7 +117,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d", - wlen, ctx->buf_total, (*plast)->type, result); + wlen, ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); Curl_bufq_skip(&(*plast)->b, wlen); DEBUGASSERT(ctx->buf_total >= wlen); ctx->buf_total -= wlen; @@ -128,7 +129,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, 0); CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d", - ctx->buf_total, (*plast)->type, result); + ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); } if(Curl_bufq_is_empty(&(*plast)->b)) { @@ -165,7 +167,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, wtype &= ~CLIENTWRITE_EOS; result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d", - wlen, blen, wtype, result); + wlen, blen, (unsigned int)wtype, (int)result); if(result) return result; buf += wlen; @@ -191,8 +193,8 @@ static CURLcode cw_pause_write(struct Curl_easy *data, result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten); } CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, " - "total=%zu -> %d", nwritten, type, ctx->buf_total + wlen, - result); + "total=%zu -> %d", nwritten, (unsigned int)type, + ctx->buf_total + wlen, (int)result); if(result) return result; buf += nwritten; diff --git a/deps/curl/lib/dict.c b/deps/curl/lib/dict.c index 7b83c6cff1..db25d5a721 100644 --- a/deps/curl/lib/dict.c +++ b/deps/curl/lib/dict.c @@ -148,7 +148,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ - /* url-decode path before further evaluation */ + /* URL-decode path before further evaluation */ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); if(result) return result; diff --git a/deps/curl/lib/dllmain.c b/deps/curl/lib/dllmain.c index f715b6d301..5aa6565010 100644 --- a/deps/curl/lib/dllmain.c +++ b/deps/curl/lib/dllmain.c @@ -31,7 +31,7 @@ #if defined(_WIN32) && !defined(CURL_STATICLIB) #if defined(USE_OPENSSL) && \ - !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \ + !defined(OPENSSL_IS_AWSLC) && !defined(OPENSSL_IS_BORINGSSL) && \ !defined(LIBRESSL_VERSION_NUMBER) #define PREVENT_OPENSSL_MEMLEAK #endif diff --git a/deps/curl/lib/dnscache.c b/deps/curl/lib/dnscache.c index 20f6b21714..9c8341e56f 100644 --- a/deps/curl/lib/dnscache.c +++ b/deps/curl/lib/dnscache.c @@ -351,7 +351,7 @@ UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, if(num_addrs > 1) { struct Curl_addrinfo **nodes; - CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + CURL_TRC_DNS(data, "Shuffling %d addresses", num_addrs); nodes = curlx_malloc(num_addrs * sizeof(*nodes)); if(nodes) { @@ -616,20 +616,6 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } -struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, - struct Curl_dns_entry *dns) -{ - if(!dns) - return NULL; - else { - struct Curl_dnscache *dnscache = dnscache_get(data); - dnscache_lock(data, dnscache); - dns->refcount++; - dnscache_unlock(data, dnscache); - return dns; - } -} - /* * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry. * When the reference count reaches 0, the entry is destroyed. It is important @@ -789,7 +775,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) result = Curl_str2addr(address, port, &ai); if(result) { - infof(data, "Resolve address '%s' found illegal", address); + infof(data, "Resolve IP address '%s' found is illegal", address); goto err; } @@ -842,7 +828,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); } - /* put this new host in the cache, an overridy for ALL dns queries */ + /* put this new host in the cache, an override for ALL dns queries */ dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, &head, curlx_str(&source), curlx_strlen(&source), port, permanent); diff --git a/deps/curl/lib/dnscache.h b/deps/curl/lib/dnscache.h index ebe25f6dd4..9239977cf8 100644 --- a/deps/curl/lib/dnscache.h +++ b/deps/curl/lib/dnscache.h @@ -80,11 +80,6 @@ void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, struct Curl_https_rrinfo *hinfo); #endif /* USE_HTTPSRR */ -/* Increase the ref counter and return it for storing in another place. - * May be called with NULL, in which case it returns NULL. */ -struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, - struct Curl_dns_entry *dns); - /* unlink a dns entry, frees all resources if it was the last reference. * Always clears `*pdns`` */ void Curl_dns_entry_unlink(struct Curl_easy *data, diff --git a/deps/curl/lib/doh.c b/deps/curl/lib/doh.c index 30441358ca..8b643aa0b4 100644 --- a/deps/curl/lib/doh.c +++ b/deps/curl/lib/doh.c @@ -259,7 +259,6 @@ static void doh_probe_done(struct Curl_easy *data, result = curlx_dyn_addn(&dohp->probe_resp[i].body, curlx_dyn_ptr(&doh_req->resp_body), curlx_dyn_len(&doh_req->resp_body)); - curlx_dyn_free(&doh_req->resp_body); } Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); @@ -319,7 +318,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, sizeof(doh_req->req_body), &doh_req->req_body_len); if(d) { - failf(data, "Failed to encode DoH packet [%d]", d); + failf(data, "Failed to encode DoH packet [%d]", (int)d); result = CURLE_OUT_OF_MEMORY; goto error; } @@ -418,7 +417,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, } (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, - (long)data->set.ssl.primary.ssl_options); + ((long)data->set.ssl.primary.ssl_options & + ~CURLSSLOPT_AUTO_CLIENT_CERT)); doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ @@ -724,7 +724,9 @@ UNITTEST void de_init(struct dohentry *de) curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } -/* @unittest 1655 */ +/* TTL value cap */ +#define MAX_DNS_TTL 86400U /* 24 hours */ +/* @unittest 1650 */ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -794,6 +796,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, return DOH_DNS_OUT_OF_RANGE; ttl = doh_get32bit(doh, index); + if(ttl > MAX_DNS_TTL) + ttl = MAX_DNS_TTL; if(ttl < d->ttl) d->ttl = ttl; index += 4; @@ -1087,10 +1091,14 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, DEBUGASSERT(buf && remaining && dnsname); if(!buf || !remaining || !dnsname || !*remaining) return CURLE_OUT_OF_MEMORY; - curlx_dyn_init(&thename, CURL_MAXLEN_host_name); + curlx_dyn_init(&thename, CURL_MAXLEN_HOST_NAME); rem = *remaining; cp = *buf; clen = *cp++; + /* RFC 9460 says it must be uncompressed */ + if(clen > 63) + return CURLE_WEIRD_SERVER_REPLY; + if(clen == 0) { /* special case - return "." as name */ if(curlx_dyn_addn(&thename, ".", 1)) @@ -1112,6 +1120,11 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, return CURLE_OUT_OF_MEMORY; } clen = *cp++; + if(clen > 63) { + /* invalid format */ + curlx_dyn_free(&thename); + return CURLE_WEIRD_SERVER_REPLY; + } } *buf = cp; *remaining = rem - 1; diff --git a/deps/curl/lib/doh.h b/deps/curl/lib/doh.h index 428b230a58..cd2aad9254 100644 --- a/deps/curl/lib/doh.h +++ b/deps/curl/lib/doh.h @@ -167,7 +167,6 @@ void Curl_doh_cleanup(struct Curl_easy *data, struct Curl_resolv_async *async); #define Curl_doh_wanted(d) (!!(d)->set.doh) - #else /* CURL_DISABLE_DOH */ #define Curl_doh(a, b) NULL #define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST diff --git a/deps/curl/lib/easy.c b/deps/curl/lib/easy.c index a472d6ddc5..d60bdaed7b 100644 --- a/deps/curl/lib/easy.c +++ b/deps/curl/lib/easy.c @@ -78,8 +78,8 @@ #include "easy_lock.h" /* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized; -static long easy_init_flags; +static unsigned int initialized; +static long easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE @@ -942,7 +942,7 @@ static void dupeasy_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /* @@ -1052,6 +1052,11 @@ CURL *curl_easy_duphandle(CURL *curl) (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, outcurl->set.str[STRING_HSTS]); (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); + + /* Copy entries learned at runtime. (E.g. Strict-Transport-Security + headers.) */ + if(Curl_hsts_copy(outcurl->hsts, data->hsts)) + goto fail; } #endif diff --git a/deps/curl/lib/fake_addrinfo.c b/deps/curl/lib/fake_addrinfo.c index 5a89202064..6b04b5d2f5 100644 --- a/deps/curl/lib/fake_addrinfo.c +++ b/deps/curl/lib/fake_addrinfo.c @@ -64,7 +64,7 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) const char *name = aihead->name; /* traverse the addrinfo list */ - for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) { + for(ai = aihead->nodes; ai; ai = ai->ai_next) { size_t ss_size; size_t namelen = name ? strlen(name) + 1 : 0; /* ignore elements with unsupported address family, diff --git a/deps/curl/lib/formdata.c b/deps/curl/lib/formdata.c index 3619c15bb1..681902db71 100644 --- a/deps/curl/lib/formdata.c +++ b/deps/curl/lib/formdata.c @@ -154,7 +154,7 @@ static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent) static void free_formlist(struct FormInfo *ptr) { - for(; ptr != NULL; ptr = ptr->more) { + for(; ptr; ptr = ptr->more) { Curl_bufref_free(&ptr->name); Curl_bufref_free(&ptr->value); Curl_bufref_free(&ptr->contenttype); @@ -184,7 +184,7 @@ static void free_formlist(struct FormInfo *ptr) * * name/value pair where only the content pointer is remembered: * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, + * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10L, * CURLFORM_END); * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) * @@ -223,7 +223,7 @@ static CURLFORMcode FormAddCheck(struct FormInfo *first_form, /* go through the list, check for completeness and if everything is * alright add the HttpPost item otherwise set retval accordingly */ - for(form = first_form; form != NULL; form = form->more) { + for(form = first_form; form; form = form->more) { const char *name = Curl_bufref_ptr(&form->name); if(((!name || !Curl_bufref_ptr(&form->value)) && !post) || diff --git a/deps/curl/lib/ftp-int.h b/deps/curl/lib/ftp-int.h index 68d26f3327..8d7e141274 100644 --- a/deps/curl/lib/ftp-int.h +++ b/deps/curl/lib/ftp-int.h @@ -113,10 +113,10 @@ struct ftp_conn { char *account; char *alternative_to_user; char *entrypath; /* the PWD reply when we logged on */ - const char *file; /* url-decoded filename (or path), points into rawpath */ + const char *file; /* URL-decoded filename (or path), points into rawpath */ char *rawpath; /* URL decoded, allocated, version of the path */ struct pathcomp *dirs; /* allocated array for path components */ - char *prevpath; /* url-decoded conn->path from the previous transfer */ + char *prevpath; /* URL-decoded conn->path from the previous transfer */ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a and others (A/I or zero) */ char *server_os; /* The target server operating system. */ diff --git a/deps/curl/lib/ftp.c b/deps/curl/lib/ftp.c index 9d3700df5f..05d17064a0 100644 --- a/deps/curl/lib/ftp.c +++ b/deps/curl/lib/ftp.c @@ -355,7 +355,6 @@ static void close_secondarysocket(struct Curl_easy *data, { (void)ftpc; CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc)); - Curl_conn_close(data, SECONDARYSOCKET); Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } @@ -733,7 +732,7 @@ static CURLcode getftpresponse(struct Curl_easy *data, pp->pending_resp = FALSE; CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d", - result, *nreadp, *ftpcodep); + (int)result, *nreadp, *ftpcodep); return result; } @@ -743,7 +742,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) { ftpc->ftp_trying_alternative = FALSE; ftp_state(data, ftpc, FTP_USER); @@ -1084,7 +1083,7 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data, curl_socket_t *portsockp) { char buffer[STRERROR_LEN]; - int error = 0; + int sockerr = 0; const struct Curl_addrinfo *ai; CURLcode result = CURLE_FTP_PORT_FAILED; @@ -1096,14 +1095,14 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data, if(result == CURLE_OUT_OF_MEMORY) return result; result = CURLE_FTP_PORT_FAILED; - error = SOCKERRNO; + sockerr = SOCKERRNO; continue; } break; } if(!ai) { failf(data, "socket failure: %s", - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } *aip = ai; @@ -1132,7 +1131,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, #endif char buffer[STRERROR_LEN]; unsigned short port; - int error; + int sockerr; memcpy(sa, ai->ai_addr, ai->ai_addrlen); *sslen_io = ai->ai_addrlen; @@ -1145,13 +1144,13 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, sa6->sin6_port = htons(port); #endif if(bind(portsock, sa, *sslen_io)) { - error = SOCKERRNO; - if(non_local && (error == SOCKEADDRNOTAVAIL)) { + sockerr = SOCKERRNO; + if(non_local && (sockerr == SOCKEADDRNOTAVAIL)) { /* The requested bind address is not local. Use the address used for * the control connection instead and restart the port loop. */ infof(data, "bind(port=%hu) on non-local address failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); *sslen_io = sizeof(*ss); if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) { @@ -1163,9 +1162,9 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, non_local = FALSE; /* do not try this again */ continue; } - if(error != SOCKEADDRINUSE && error != SOCKEACCES) { + if(sockerr != SOCKEADDRINUSE && sockerr != SOCKEACCES) { failf(data, "bind(port=%hu) failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } } @@ -1188,8 +1187,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", - port); + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", port); return CURLE_OK; } @@ -1276,8 +1274,7 @@ static CURLcode ftp_port_send_command(struct Curl_easy *data, * EPRT |2|1080::8:800:200C:417A|5282| */ result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], - sa->sa_family == AF_INET ? 1 : 2, - myhost, port); + sa->sa_family == AF_INET ? 1 : 2, myhost, port); if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); @@ -1393,10 +1390,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); } else { - /* successfully set up the listen socket filter. SSL needed? */ + /* successfully set up the listen socket filter. SSL needed? + * Use the control connections origin for cert verification. */ if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), + conn, SECONDARYSOCKET); } conn->bits.do_more = FALSE; Curl_pgrsTime(data, TIMER_STARTACCEPT); @@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(conn->bits.ipv6 -#ifndef CURL_DISABLE_PROXY - && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) -#endif - ) { + if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) { /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ failf(data, "Failed EPSV attempt, exiting"); return CURLE_WEIRD_SERVER_REPLY; @@ -2019,8 +2015,8 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) the effective control connection address is the proxy address, not the ftp host. */ #ifndef CURL_DISABLE_PROXY - if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - *newhostp = curlx_strdup(conn->host.name); + if(Curl_conn_is_tunneling(conn, FIRSTSOCKET)) + *newhostp = curlx_strdup(conn->origin->hostname); else #endif if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && @@ -2059,8 +2055,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, { struct connectdata *conn = data->conn; CURLcode result; - struct Curl_dns_entry *dns = NULL; - unsigned short connectport; /* the local port connect() should use! */ const struct pingpong *pp = &ftpc->pp; char *newhost = NULL; unsigned short newport = 0; @@ -2075,7 +2069,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ptr++; /* |||12345| */ sep = ptr[0]; - if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + if(sep && (ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { const char *p = &ptr[3]; curl_off_t num; if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) { @@ -2125,7 +2119,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* told to ignore the remotely given IP but instead use the host we used for the control connection */ infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", - ip[0], ip[1], ip[2], ip[3], conn->host.name); + ip[0], ip[1], ip[2], ip[3], conn->origin->hostname); result = ftp_control_addr_dup(data, &newhost); if(result) return result; @@ -2147,67 +2141,25 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, return CURLE_FTP_WEIRD_PASV_REPLY; } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - /* This connection uses a proxy and we need to connect to the proxy again - * here. We do not want to rely on a former host lookup that might have - * expired now, instead we remake the lookup here and now! */ - struct ip_quadruple ipquad; - bool is_ipv6; - const char * const host_name = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; - - result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, - &is_ipv6, &ipquad); - if(result) - goto error; - - (void)Curl_resolv_blocking( - data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, - host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), - &dns); - /* we connect to the proxy's port */ - connectport = (unsigned short)ipquad.remote_port; - - if(!dns) { - failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - } - else -#endif - { - /* normal, direct, ftp connection */ - DEBUGASSERT(newhost); - - /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { - curlx_free(newhost); - result = ftp_control_addr_dup(data, &newhost); - if(result) - goto error; - } - - (void)Curl_resolv_blocking( - data, Curl_resolv_dns_queries(data, conn->ip_version), - newhost, newport, Curl_conn_get_transport(data, conn), &dns); - connectport = newport; /* we connect to the remote port */ + DEBUGASSERT(newhost); + Curl_peer_unlink(&conn->origin2); + result = Curl_peer_create(data, conn->scheme, newhost, newport, + &conn->origin2); + if(result) + goto error; - if(!dns) { - failf(data, "cannot resolve new host %s:%hu", newhost, connectport); - result = CURLE_FTP_CANT_GET_HOST; + /* If FIRSTSOCKET goes via another peer, SECONDARY needs as well, + * but with its new port. */ + if(conn->via_peer) { + Curl_peer_unlink(&conn->via_peer2); + result = Curl_peer_create(data, conn->via_peer->scheme, + conn->via_peer->hostname, newport, + &conn->via_peer2); + if(result) goto error; - } } - DEBUGASSERT(newhost); - curlx_free(conn->secondaryhostname); - conn->secondary_port = newport; - conn->secondaryhostname = newhost; - newhost = NULL; - - result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); @@ -2227,13 +2179,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, #ifdef CURLVERBOSE if(data->set.verbose) { - /* Dump information about this second connection when we have issued a PASV - * command before and thus we have connected to a possibly new IP address. - */ - char buf[256]; - Curl_printable_address(dns->addr, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", - conn->secondaryhostname, buf, connectport); + /* Dump information about this second connection when we have issued + * a PASV command. */ + infof(data, "Connecting to %s port %d", + conn->origin2->hostname, conn->origin2->port); } #endif @@ -2241,7 +2190,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ error: - Curl_dns_entry_unlink(data, &dns); curlx_free(newhost); return result; } @@ -2267,10 +2215,10 @@ static CURLcode ftp_statemach(struct Curl_easy *data, * This function shall be called when the second FTP (data) connection is * connected. * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * 'more' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK * (which is for when PASV is being sent to retry a failed EPSV). */ -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +static CURLcode ftp_do_more(struct Curl_easy *data, domore *more) { struct connectdata *conn = data->conn; struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); @@ -2285,7 +2233,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc || !ftp) return CURLE_FAILED_INIT; - *completep = 0; /* default to stay in the state */ + *more = DOMORE_INCOMPLETE; /* default to stay in the state */ /* if the second connection has been set up, try to connect it fully * to the remote host. This may not complete at this time, for several @@ -2303,7 +2251,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result || (!connected && !is_eptr && !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { if(result && !is_eptr && (ftpc->count1 == 0)) { - *completep = -1; /* go back to DOING please */ + *more = DOMORE_GOBACK; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ return ftp_epsv_disable(data, ftpc, conn); } @@ -2316,7 +2264,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) They are only done to kickstart the do_more state */ result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; /* if we got an error or if we do not wait for a data connection return immediately */ @@ -2326,7 +2275,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) /* if we reach the end of the FTP state machine here, *complete will be TRUE but so is ftpc->wait_data_conn, which says we need to wait for the data connection and therefore we are not actually complete */ - *completep = 0; + *more = DOMORE_INCOMPLETE; } if(ftp->transfer <= PPTRANSFER_INFO) { @@ -2348,8 +2297,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result) return result; - *completep = 1; /* this state is now complete when the server has - connected back to us */ + *more = DOMORE_DONE; /* this state is now complete when the server has + connected back to us */ } else { result = ftp_check_ctrl_on_data_wait(data, ftpc); @@ -2368,7 +2317,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * deemed necessary and directly sent `STORE name`. If this was * then complete, but we are still waiting on the data connection, * the transfer has not been initiated yet. */ - *completep = (int)(ftpc->wait_data_conn ? 0 : complete); + *more = (!ftpc->wait_data_conn && complete) ? + DOMORE_DONE : DOMORE_INCOMPLETE; } else { /* download */ @@ -2411,7 +2361,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; } return result; } @@ -2421,7 +2372,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ - *completep = 1; + *more = DOMORE_DONE; CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), (int)result); } @@ -2436,7 +2387,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) { if(connected) { - int completed; + domore completed; CURLcode result = ftp_do_more(data, &completed); if(result) { @@ -2950,7 +2901,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(data->conn->bits.ftp_use_control_ssl) { + if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { /* PBSZ = PROTECTION BUFFER SIZE. The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: @@ -2986,7 +2937,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ - result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd); + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", + Curl_creds_passwd(data->conn->creds)); if(!result) ftp_state(data, ftpc, FTP_PASS); } @@ -3161,8 +3113,8 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; if(ftpcode == 230) { /* 230 User logged in - already! Take as 220 if TLS required. */ - if(data->set.use_ssl <= CURLUSESSL_TRY || - conn->bits.ftp_use_control_ssl) + if(ftpc->use_ssl <= CURLUSESSL_TRY || + Curl_conn_is_ssl(conn, FIRSTSOCKET)) return ftp_state_user_resp(data, ftpc, ftpcode); } else if(ftpcode != 220) { @@ -3171,7 +3123,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, return CURLE_WEIRD_SERVER_REPLY; } - if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { + if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* We do not have an SSL/TLS control connection yet, but FTPS is requested. Try an FTPS connection now */ @@ -3243,16 +3195,17 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* this was BLOCKING, keep it so for now */ bool done; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) { /* we failed and bail out */ return CURLE_USE_SSL_FAILED; } } + /* BLOCKING */ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); if(!result) { conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ - conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ result = ftp_state_user(data, ftpc, conn); } } @@ -3264,7 +3217,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* remain in this same state */ } else { - if(data->set.use_ssl > CURLUSESSL_TRY) + if(ftpc->use_ssl > CURLUSESSL_TRY) /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ result = CURLE_USE_SSL_FAILED; else @@ -3285,7 +3238,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PBSZ: result = Curl_pp_sendf(data, &ftpc->pp, "PROT %c", - data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); + ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); if(!result) ftp_state(data, ftpc, FTP_PROT); break; @@ -3293,10 +3246,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PROT: if(ftpcode / 100 == 2) /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL); + conn->bits.ftp_use_data_ssl = (ftpc->use_ssl != CURLUSESSL_CONTROL); /* FTP servers typically responds with 500 if they decide to reject our 'P' request */ - else if(data->set.use_ssl > CURLUSESSL_CONTROL) + else if(ftpc->use_ssl > CURLUSESSL_CONTROL) /* we failed and bails out */ return CURLE_USE_SSL_FAILED; @@ -3572,7 +3525,6 @@ static CURLcode ftp_connect(struct Curl_easy *data, result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) return result; - conn->bits.ftp_use_control_ssl = TRUE; } Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ @@ -3641,30 +3593,10 @@ static CURLcode ftp_sendquote(struct Curl_easy *data, return CURLE_OK; } -/*********************************************************************** - * - * ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, - bool premature) +static CURLcode ftp_done_status(struct connectdata *conn, + struct ftp_conn *ftpc, CURLcode status, + bool premature) { - struct connectdata *conn = data->conn; - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp; - size_t nread; - int ftpcode; - CURLcode result = CURLE_OK; - - if(!ftp || !ftpc) - return CURLE_OK; - - pp = &ftpc->pp; switch(status) { case CURLE_BAD_DOWNLOAD_RESUME: case CURLE_FTP_WEIRD_PASV_REPLY: @@ -3693,10 +3625,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the current path, as this connection is going */ connclose(conn, "FTP ended with bad error code"); - result = status; /* use the already set error code */ - break; + return status; /* use the already set error code */ } + return CURLE_OK; +} +static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc) +{ if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, TRUE); @@ -3706,14 +3641,18 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } ftpc->known_filesize = -1; } +} +static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - curlx_free(ftpc->prevpath); - ftpc->prevpath = NULL; /* no path remembering */ + curlx_safefree(ftpc->prevpath); /* no path remembering */ } else { /* remember working directory for connection reuse */ const char *rawPath = ftpc->rawpath; @@ -3740,13 +3679,17 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(ftpc->prevpath) infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); } +} - /* shut down the socket to inform the server we are done */ - +static CURLcode ftp_done_secondary_socket(struct Curl_easy *data, + struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ - result = Curl_pp_sendf(data, pp, "%s", "ABOR"); + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR"); if(result) { failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); @@ -3757,16 +3700,27 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, close_secondarysocket(data, ftpc); } + return result; +} + +static CURLcode ftp_done_control_reply(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ + struct connectdata *conn = data->conn; + size_t nread; + int ftpcode; if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && - pp->pending_resp && !premature) { + ftpc->pp.pending_resp && !premature) { /* * Let's see what the server says about the transfer we performed, but * lower the timeout as sometimes this connection has died while the data * has been transferred. This happens when doing through NATs etc that * abandon old silent connections. */ - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */ result = getftpresponse(data, &nread, &ftpcode); if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { @@ -3803,7 +3757,14 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } } + return result; +} +static CURLcode ftp_done_check_partial(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ if(result || premature) /* the response code from the transfer showed an error already so no use checking further */ @@ -3837,6 +3798,35 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, result = CURLE_FTP_COULDNT_RETR_FILE; } } + return result; +} + +/*********************************************************************** + * + * ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + CURLcode result; + + if(!ftp || !ftpc) + return CURLE_OK; + + result = ftp_done_status(data->conn, ftpc, status, premature); + + ftp_done_wildcard(data, ftpc); + ftp_done_path(data, ftpc, result); + result = ftp_done_secondary_socket(data, ftpc, result); + result = ftp_done_control_reply(data, ftpc, ftp, result, premature); + result = ftp_done_check_partial(data, ftpc, ftp, result, premature); /* clear these for next connection */ ftp->transfer = PPTRANSFER_BODY; @@ -3845,7 +3835,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, /* Send any post-transfer QUOTE strings? */ if(!status && !result && !premature && data->set.postquote) result = ftp_sendquote(data, ftpc, data->set.postquote); - CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result); return result; } @@ -4454,7 +4444,8 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, ftpc->use_ssl = data->set.use_ssl; ftpc->ccc = data->set.ftp_ccc; - CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), + (int)result); return result; } @@ -4462,14 +4453,22 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) { struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN); struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + /* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */ if(!nftpc || !cftpc || Curl_timestrcmp(nftpc->account, cftpc->account) || Curl_timestrcmp(nftpc->alternative_to_user, cftpc->alternative_to_user) || - (nftpc->use_ssl != cftpc->use_ssl) || (nftpc->ccc != cftpc->ccc)) return FALSE; + /* A mismatch on `use_ssl` MUST have been found in connection matching + * before we come here. This is a check on MAYBE/MUST use of STARTTLS and + * it only works on FTP. But IMAP/SMTP etc have the same `use_ssl` and + * no extra match like FTP. We lack tests in this area, so let FTP fail + * loudly here to help other cases. */ + if(nftpc->use_ssl > cftpc->use_ssl) { + DEBUGASSERT(0); + return FALSE; + } return TRUE; } diff --git a/deps/curl/lib/ftplistparser.c b/deps/curl/lib/ftplistparser.c index b5d9338c1b..23fbd1f07c 100644 --- a/deps/curl/lib/ftplistparser.c +++ b/deps/curl/lib/ftplistparser.c @@ -196,13 +196,11 @@ void Curl_wildcard_dtor(struct WildcardData **wcp) wc->dtor = ZERO_NULL; wc->ftpwc = NULL; } - DEBUGASSERT(wc->ftpwc == NULL); + DEBUGASSERT(!wc->ftpwc); Curl_llist_destroy(&wc->filelist, NULL); - curlx_free(wc->path); - wc->path = NULL; - curlx_free(wc->pattern); - wc->pattern = NULL; + curlx_safefree(wc->path); + curlx_safefree(wc->pattern); wc->state = CURLWC_INIT; curlx_free(wc); *wcp = NULL; @@ -312,8 +310,9 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, str + parser->offsets.group : NULL; finfo->strings.perm = parser->offsets.perm ? str + parser->offsets.perm : NULL; - finfo->strings.target = parser->offsets.symlink_target ? - str + parser->offsets.symlink_target : NULL; + finfo->strings.target = parser->offsets.symlink_target && + (finfo->filetype == CURLFILETYPE_SYMLINK) ? + str + parser->offsets.symlink_target : NULL; finfo->strings.time = str + parser->offsets.time; finfo->strings.user = parser->offsets.user ? str + parser->offsets.user : NULL; @@ -940,7 +939,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, parser->item_length++; if(c == ' ') { mem[parser->item_offset + parser->item_length - 1] = 0; - if(strcmp("", mem + parser->item_offset) == 0) { + if(!strcmp("", mem + parser->item_offset)) { finfo->filetype = CURLFILETYPE_DIRECTORY; finfo->size = 0; } diff --git a/deps/curl/lib/functypes.h b/deps/curl/lib/functypes.h index 887c2612ef..3938295dd1 100644 --- a/deps/curl/lib/functypes.h +++ b/deps/curl/lib/functypes.h @@ -48,7 +48,7 @@ #define SEND_TYPE_ARG3 int #define SEND_TYPE_RETV int -#elif defined(__AMIGA__) /* Any AmigaOS flavour */ +#elif defined(__AMIGA__) /* Any AmigaOS flavor */ /* long recv(long, char *, long, long); */ #define RECV_TYPE_ARG1 long diff --git a/deps/curl/lib/getinfo.c b/deps/curl/lib/getinfo.c index fab63e669a..fde4aa4ef2 100644 --- a/deps/curl/lib/getinfo.c +++ b/deps/curl/lib/getinfo.c @@ -67,11 +67,8 @@ void Curl_initinfo(struct Curl_easy *data) info->httpauthpicked = 0; info->numconnects = 0; - curlx_free(info->contenttype); - info->contenttype = NULL; - - curlx_free(info->wouldredirect); - info->wouldredirect = NULL; + curlx_safefree(info->contenttype); + curlx_safefree(info->wouldredirect); memset(&info->primary, 0, sizeof(info->primary)); info->retry_after = 0; diff --git a/deps/curl/lib/gopher.c b/deps/curl/lib/gopher.c index f087121d07..039697466b 100644 --- a/deps/curl/lib/gopher.c +++ b/deps/curl/lib/gopher.c @@ -133,7 +133,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - /* Do not busyloop. The entire loop thing is a work-around as it causes a + /* Do not busyloop. The entire loop thing is a workaround as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that are not possible to send now will be sent in the doing function repeatedly diff --git a/deps/curl/lib/headers.c b/deps/curl/lib/headers.c index 195e12b371..b290a9e5b3 100644 --- a/deps/curl/lib/headers.c +++ b/deps/curl/lib/headers.c @@ -305,7 +305,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data, CURLH_HEADER))); CURLcode result = Curl_headers_push(data, buf, blen, htype); CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", - htype, blen, result); + htype, blen, (int)result); if(result) return result; } diff --git a/deps/curl/lib/hostip.c b/deps/curl/lib/hostip.c index 85f53c4ef4..a18d9a62bb 100644 --- a/deps/curl/lib/hostip.c +++ b/deps/curl/lib/hostip.c @@ -70,10 +70,6 @@ #define USE_ALARM_TIMEOUT #endif -#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ - -#define MAX_DNS_CACHE_SIZE 29999 - #define RESOLV_FAIL(for_proxy) \ ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST) @@ -511,7 +507,6 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, return Curl_async_get_ai(data, async, ai_family, index); } - #ifdef USE_HTTPSRR const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) @@ -529,7 +524,6 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) return TRUE; return Curl_async_knows_https(data, async); } - #endif /* USE_HTTPSRR */ #endif /* USE_CURL_ASYNC */ @@ -740,7 +734,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, failf(data, "Could not resolve: %s:%u", hostname, port); } else { - failf(data, "Error %d resolving %s:%u", result, hostname, port); + failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); } } else if(cache_dns && *pdns) { @@ -795,9 +789,6 @@ CURL_NORETURN static void alarmfunc(int sig) (void)sig; siglongjmp(curl_jmpenv, 1); } -#endif /* USE_ALARM_TIMEOUT */ - -#ifdef USE_ALARM_TIMEOUT static CURLcode resolv_alarm_timeout(struct Curl_easy *data, uint8_t dns_queries, @@ -935,6 +926,33 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, #endif /* USE_ALARM_TIMEOUT */ +#ifdef USE_UNIX_SOCKETS +static CURLcode resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns) +{ + struct Curl_addrinfo *addr; + CURLcode result; + + DEBUGASSERT(unix_path); + *pdns = NULL; + + result = Curl_unix2addr(unix_path, abstract_path, &addr); + if(result) { + if(result == CURLE_TOO_LARGE) { + /* Long paths are not supported for now */ + failf(data, "Unix socket path too long: '%s'", unix_path); + result = CURLE_COULDNT_RESOLVE_HOST; + } + return result; + } + + *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} +#endif /* USE_UNIX_SOCKETS */ + /* * Curl_resolv() is the main name resolve function within libcurl. It resolves * a name and returns a pointer to the entry in the 'entry' argument. This @@ -956,16 +974,14 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, * any other CURLcode error, *pdns == NULL */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, uint32_t *presolv_id, struct Curl_dns_entry **pdns) { - DEBUGASSERT(hostname && *hostname); *presolv_id = 0; *pdns = NULL; @@ -975,14 +991,23 @@ CURLcode Curl_resolv(struct Curl_easy *data, else if(!timeout_ms) timeout_ms = CURL_TIMEOUT_RESOLVE_MS; +#ifdef USE_UNIX_SOCKETS + if(peer->unix_socket) + return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); +#else + if(peer->unix_socket) + return hostip_resolv_failed(data, peer->hostname, for_proxy); +#endif + #ifdef USE_ALARM_TIMEOUT if(timeout_ms && data->set.no_signal) { /* Cannot use ALARM when signals are disabled */ timeout_ms = 0; } if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, presolv_id, pdns); + return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, presolv_id, + pdns); } #endif /* !USE_ALARM_TIMEOUT */ @@ -991,8 +1016,9 @@ CURLcode Curl_resolv(struct Curl_easy *data, infof(data, "timeout on name lookup is not supported"); #endif - return hostip_resolv(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, TRUE, presolv_id, pdns); + return hostip_resolv(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, TRUE, presolv_id, + pdns); } #ifdef USE_CURL_ASYNC @@ -1047,7 +1073,7 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, } else if(result) { failf(data, "Error %d resolving %s:%u", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } @@ -1101,30 +1127,3 @@ void Curl_resolv_destroy_all(struct Curl_easy *data) } #endif /* USE_CURL_ASYNC */ - -#ifdef USE_UNIX_SOCKETS -CURLcode Curl_resolv_unix(struct Curl_easy *data, - const char *unix_path, - bool abstract_path, - struct Curl_dns_entry **pdns) -{ - struct Curl_addrinfo *addr; - CURLcode result; - - DEBUGASSERT(unix_path); - *pdns = NULL; - - result = Curl_unix2addr(unix_path, abstract_path, &addr); - if(result) { - if(result == CURLE_TOO_LARGE) { - /* Long paths are not supported for now */ - failf(data, "Unix socket path too long: '%s'", unix_path); - result = CURLE_COULDNT_RESOLVE_HOST; - } - return result; - } - - *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); - return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; -} -#endif /* USE_UNIX_SOCKETS */ diff --git a/deps/curl/lib/hostip.h b/deps/curl/lib/hostip.h index 780fb4dc13..45370ec48e 100644 --- a/deps/curl/lib/hostip.h +++ b/deps/curl/lib/hostip.h @@ -45,6 +45,7 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; struct Curl_dns_entry; +struct Curl_peer; /* DNS query types */ #define CURL_DNSQ_A (1U << 0) @@ -96,9 +97,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, @@ -184,11 +184,4 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, uint8_t transport); #endif -#ifdef USE_UNIX_SOCKETS -CURLcode Curl_resolv_unix(struct Curl_easy *data, - const char *unix_path, - bool abstract_path, - struct Curl_dns_entry **pdns); -#endif - #endif /* HEADER_CURL_HOSTIP_H */ diff --git a/deps/curl/lib/hostip4.c b/deps/curl/lib/hostip4.c index fb35e3992c..c8cc4dc340 100644 --- a/deps/curl/lib/hostip4.c +++ b/deps/curl/lib/hostip4.c @@ -64,7 +64,7 @@ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME * has the corresponding rules. This is primarily on *nix. Note that some Unix - * flavours have thread-safe versions of the plain gethostbyname() etc. + * flavors have thread-safe versions of the plain gethostbyname() etc. * */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, diff --git a/deps/curl/lib/hsts.c b/deps/curl/lib/hsts.c index 400b4423da..b8745a5195 100644 --- a/deps/curl/lib/hsts.c +++ b/deps/curl/lib/hsts.c @@ -96,7 +96,7 @@ void Curl_hsts_cleanup(struct hsts **hp) static void hsts_append(struct hsts *h, struct stsentry *sts) { if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) { - /* It's full. Remove the first entry in the list */ + /* It is full. Remove the first entry in the list */ struct Curl_llist_node *e = Curl_llist_head(&h->list); struct stsentry *oldsts = Curl_node_elem(e); Curl_node_remove(e); @@ -121,7 +121,7 @@ static CURLcode hsts_create(struct hsts *h, struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen); if(!sts) return CURLE_OUT_OF_MEMORY; - /* the null terminator is already there */ + /* the null-terminator is already there */ memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; @@ -130,6 +130,81 @@ static CURLcode hsts_create(struct hsts *h, return CURLE_OK; } +/* Copy all live entries from src into dst. Used by curl_easy_duphandle so the + * clone inherits entries learned at runtime. E.g. Strict-Transport-Security. + */ +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src) +{ + struct Curl_llist_node *e; + time_t now = time(NULL); + for(e = Curl_llist_head(&src->list); e; e = Curl_node_next(e)) { + struct stsentry *sts = Curl_node_elem(e); + if(sts->expires > now) { + CURLcode result = hsts_create(dst, sts->host, strlen(sts->host), + sts->includeSubDomains != 0, sts->expires); + if(result) + return result; + } + } + return CURLE_OK; +} + +/* + * Return the matching HSTS entry, or NULL if the given hostname is not + * currently an HSTS one. + * + * The 'subdomain' argument tells the function if subdomain matching should be + * attempted. + * + * @unittest 1660 + */ +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain); +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain) +{ + struct stsentry *bestsub = NULL; + if(h) { + time_t now = time(NULL); + struct Curl_llist_node *e; + struct Curl_llist_node *n; + size_t blen = 0; + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; + if(hostname[hlen - 1] == '.') + /* remove the trailing dot */ + --hlen; + + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + size_t ntail; + n = Curl_node_next(e); + if(sts->expires <= now) { + /* remove expired entries */ + Curl_node_remove(&sts->node); + hsts_free(sts); + continue; + } + ntail = strlen(sts->host); + if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { + size_t offs = hlen - ntail; + if((hostname[offs - 1] == '.') && + curl_strnequal(&hostname[offs], sts->host, ntail) && + (ntail > blen)) { + /* save the tail match with the longest tail */ + bestsub = sts; + blen = ntail; + } + } + /* avoid curl_strequal because the hostname is not null-terminated */ + if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) + return sts; + } + } + return bestsub; +} + CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header) { @@ -203,7 +278,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, if(!expires) { /* remove the entry if present verbatim (without subdomain match) */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { Curl_node_remove(&sts->node); hsts_free(sts); @@ -218,7 +293,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, expires += now; /* check if it already exists */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { /* update these fields */ sts->expires = expires; @@ -230,57 +305,6 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, return CURLE_OK; } -/* - * Return TRUE if the given hostname is currently an HSTS one. - * - * The 'subdomain' argument tells the function if subdomain matching should be - * attempted. - */ -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - size_t hlen, bool subdomain) -{ - struct stsentry *bestsub = NULL; - if(h) { - time_t now = time(NULL); - struct Curl_llist_node *e; - struct Curl_llist_node *n; - size_t blen = 0; - - if((hlen > MAX_HSTS_HOSTLEN) || !hlen) - return NULL; - if(hostname[hlen - 1] == '.') - /* remove the trailing dot */ - --hlen; - - for(e = Curl_llist_head(&h->list); e; e = n) { - struct stsentry *sts = Curl_node_elem(e); - size_t ntail; - n = Curl_node_next(e); - if(sts->expires <= now) { - /* remove expired entries */ - Curl_node_remove(&sts->node); - hsts_free(sts); - continue; - } - ntail = strlen(sts->host); - if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { - size_t offs = hlen - ntail; - if((hostname[offs - 1] == '.') && - curl_strnequal(&hostname[offs], sts->host, ntail) && - (ntail > blen)) { - /* save the tail match with the longest tail */ - bestsub = sts; - blen = ntail; - } - } - /* avoid curl_strequal because the hostname is not null-terminated */ - if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) - return sts; - } - } - return bestsub; -} - /* * Send this HSTS entry to the write callback. */ @@ -437,10 +461,10 @@ static CURLcode hsts_add_host_expire(struct hsts *h, if(hostlen) { /* only add it if not already present */ - e = Curl_hsts(h, host, hostlen, subdomain); + e = hsts_check(h, host, hostlen, subdomain); if(!e) result = hsts_create(h, host, hostlen, subdomain, expires); - /* 'host' is not necessarily null terminated */ + /* 'host' is not necessarily null-terminated */ else if((hostlen == strlen(e->host) && curl_strnequal(host, e->host, hostlen))) { /* the same hostname, use the largest expire time and keep the strictest @@ -504,7 +528,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) const char *date = e.expire; if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] || e.name[MAX_HSTS_HOSTLEN]) - /* bail out if no name was stored or if a null terminator is gone */ + /* bail out if no name was stored or if a null-terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; if(!date[0]) date = UNLIMITED; @@ -610,6 +634,11 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) return result; } +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) +{ + return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE); +} + #if defined(DEBUGBUILD) || defined(UNITTESTS) #undef time #endif diff --git a/deps/curl/lib/hsts.h b/deps/curl/lib/hsts.h index 0e6585f116..08215f5eaa 100644 --- a/deps/curl/lib/hsts.h +++ b/deps/curl/lib/hsts.h @@ -25,9 +25,13 @@ ***************************************************************************/ #include "curl_setup.h" +struct hsts; + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" +struct Curl_peer; + #define MAX_HSTS_ENTRIES 10000 #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -50,10 +54,9 @@ struct hsts { struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header); -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - size_t hlen, bool subdomain); CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadfile(struct Curl_easy *data, @@ -61,6 +64,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); CURLcode Curl_hsts_loadfiles(struct Curl_easy *data); + +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest); + #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x, y) CURLE_OK diff --git a/deps/curl/lib/http.c b/deps/curl/lib/http.c index 9118c7e716..83d0e01152 100644 --- a/deps/curl/lib/http.c +++ b/deps/curl/lib/http.c @@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, { struct curl_slist *head; - for(head = (conn->bits.proxy && data->set.sep_headers) ? + for(head = (conn->http_proxy.peer && data->set.sep_headers) ? data->set.proxyheaders : data->set.headers; head; head = head->next) { if(curl_strnequal(head->data, thisheader, thislen) && @@ -250,34 +250,38 @@ char *Curl_copy_header_value(const char *header) * * Returns CURLcode. */ -static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) +static CURLcode http_output_basic(struct Curl_easy *data, + struct connectdata *conn, bool proxy) { size_t size = 0; char *authorization = NULL; - char **userp; - const char *user; - const char *pwd; + char **p_hd; CURLcode result; + struct Curl_creds *creds = NULL; char *out; /* credentials are unique per transfer for HTTP, do not use the ones for the connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->req.proxyuserpwd; - user = data->state.aptr.proxyuser; - pwd = data->state.aptr.proxypasswd; + p_hd = &data->req.hd_proxy_auth; + creds = conn->http_proxy.creds; #else + (void)conn; return CURLE_NOT_BUILT_IN; #endif } else { - userp = &data->req.userpwd; - user = data->state.aptr.user; - pwd = data->state.aptr.passwd; + p_hd = &data->req.hd_auth; + creds = data->state.creds; + } + + if(!creds) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } - out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + out = curl_maprintf("%s:%s", creds->user, creds->passwd); if(!out) return CURLE_OUT_OF_MEMORY; @@ -291,12 +295,12 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) goto fail; } - curlx_free(*userp); - *userp = curl_maprintf("%sAuthorization: Basic %s\r\n", - proxy ? "Proxy-" : "", - authorization); + curlx_free(*p_hd); + *p_hd = curl_maprintf("%sAuthorization: Basic %s\r\n", + proxy ? "Proxy-" : "", + authorization); curlx_free(authorization); - if(!*userp) { + if(!*p_hd) { result = CURLE_OUT_OF_MEMORY; goto fail; } @@ -320,10 +324,11 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; - userp = &data->req.userpwd; + DEBUGASSERT(Curl_creds_has_oauth_bearer(data->state.creds)); + userp = &data->req.hd_auth; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); + Curl_creds_oauth_bearer(data->state.creds)); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -333,7 +338,6 @@ static CURLcode http_output_bearer(struct Curl_easy *data) fail: return result; } - #endif #endif @@ -343,8 +347,10 @@ static CURLcode http_output_bearer(struct Curl_easy *data) * * return TRUE if one was picked */ -static bool pickoneauth(struct auth *pick, unsigned long mask) +static bool pickoneauth(struct auth *pick, unsigned long mask, + struct Curl_creds *creds) { + bool have_user_pass = Curl_creds_has_user_or_pass(creds); bool picked; /* only deal with authentication we want */ unsigned long avail = pick->avail & pick->want & mask; @@ -352,20 +358,20 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) /* The order of these checks is highly relevant, as this will be the order of preference in case of the existence of multiple accepted types. */ - if(avail & CURLAUTH_NEGOTIATE) + if(avail & CURLAUTH_NEGOTIATE) /* available on empty creds */ pick->picked = CURLAUTH_NEGOTIATE; #ifndef CURL_DISABLE_BEARER_AUTH - else if(avail & CURLAUTH_BEARER) + else if((avail & CURLAUTH_BEARER) && Curl_creds_has_oauth_bearer(creds)) pick->picked = CURLAUTH_BEARER; #endif #ifndef CURL_DISABLE_DIGEST_AUTH - else if(avail & CURLAUTH_DIGEST) + else if((avail & CURLAUTH_DIGEST) && have_user_pass) pick->picked = CURLAUTH_DIGEST; #endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; #ifndef CURL_DISABLE_BASIC_AUTH - else if(avail & CURLAUTH_BASIC) + else if((avail & CURLAUTH_BASIC) && have_user_pass) pick->picked = CURLAUTH_BASIC; #endif #ifndef CURL_DISABLE_AWS @@ -527,10 +533,10 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) * Either we are not authenticating, or we are supposed to be authenticating * something else. This is an error. */ - if((httpcode == 401) && !data->state.aptr.user) + if((httpcode == 401) && !data->state.creds) return TRUE; #ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + if((httpcode == 407) && !data->conn->http_proxy.creds) return TRUE; #endif @@ -551,7 +557,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) CURLcode result = CURLE_OK; unsigned long authmask = ~0UL; - if(!data->set.str[STRING_BEARER]) + if(!Curl_creds_has_oauth_bearer(data->state.creds)) authmask &= (unsigned long)~CURLAUTH_BEARER; if(100 <= data->req.httpcode && data->req.httpcode <= 199) @@ -561,10 +567,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authproblem) return data->set.http_fail_on_error ? CURLE_HTTP_RETURNED_ERROR : CURLE_OK; - if((data->state.aptr.user || data->set.str[STRING_BEARER]) && + if(data->state.creds && ((data->req.httpcode == 401) || (data->req.authneg && data->req.httpcode < 300))) { - pickhost = pickoneauth(&data->state.authhost, authmask); + pickhost = pickoneauth(&data->state.authhost, authmask, data->state.creds); if(!pickhost) data->state.authproblem = TRUE; else @@ -578,11 +584,12 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) } } #ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_user_passwd && + if(conn->http_proxy.creds && ((data->req.httpcode == 407) || (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, - authmask & ~CURLAUTH_BEARER); + authmask & ~CURLAUTH_BEARER, + conn->http_proxy.creds); if(!pickproxy) data->state.authproblem = TRUE; else @@ -694,14 +701,16 @@ static CURLcode output_auth_headers(struct Curl_easy *data, /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && + (proxy && conn->http_proxy.creds && + Curl_creds_has_user_or_pass(conn->http_proxy.creds) && !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && + (!proxy && data->state.creds && + Curl_creds_has_user_or_pass(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; - result = http_output_basic(data, proxy); + result = http_output_basic(data, conn, proxy); if(result) return result; } @@ -714,8 +723,7 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if(!proxy && data->set.str[STRING_BEARER] && - Curl_auth_allowed_to_host(data) && + if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); @@ -737,15 +745,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data, data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, - proxy ? (data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : "") : - (data->state.aptr.user ? - data->state.aptr.user : "")); + proxy ? (conn->http_proxy.creds ? + conn->http_proxy.creds->user : "") : + (data->state.creds ? + data->state.creds->user : "")); #else (void)proxy; infof(data, "Server auth using %s with user '%s'", - auth, data->state.aptr.user ? - data->state.aptr.user : ""); + auth, data->state.creds ? + data->state.creds->user : ""); #endif authstatus->multipass = !authstatus->done; } @@ -760,53 +768,47 @@ static CURLcode output_auth_headers(struct Curl_easy *data, return result; } -/** - * Curl_http_output_auth() setups the authentication headers for the - * host/proxy and the correct authentication - * method. data->state.authdone is set to TRUE when authentication is - * done. - * - * @param conn all information about the current connection - * @param request pointer to the request keyword - * @param path pointer to the requested path; should include query part - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" - * - * @returns CURLcode - */ CURLcode Curl_http_output_auth(struct Curl_easy *data, struct connectdata *conn, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel) /* TRUE if this is - the request setting up - the proxy tunnel */ + const char *query, + bool is_connect) { CURLcode result = CURLE_OK; struct auth *authhost; struct auth *authproxy; + const char *path_and_query = path; + char *tmp_str = NULL; DEBUGASSERT(data); - authhost = &data->state.authhost; authproxy = &data->state.authproxy; if( #ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || + (!conn->http_proxy.peer || !conn->http_proxy.creds) && #endif - data->state.aptr.user || #ifdef USE_SPNEGO - authhost->want & CURLAUTH_NEGOTIATE || - authproxy->want & CURLAUTH_NEGOTIATE || + !(authhost->want & CURLAUTH_NEGOTIATE) && + !(authproxy->want & CURLAUTH_NEGOTIATE) && #endif - data->set.str[STRING_BEARER]) - /* continue please */; - else { + !data->state.creds) { + /* no authentication with no user or password */ authhost->done = TRUE; authproxy->done = TRUE; - return CURLE_OK; /* no authentication with no user or password */ + result = CURLE_OK; + goto out; + } + + if(query) { + tmp_str = curl_maprintf("%s?%s", path, query); + if(!tmp_str) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + path_and_query = tmp_str; } if(authhost->want && !authhost->picked) @@ -823,28 +825,25 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ - if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == (curl_bit)proxytunnel)) { - result = output_auth_headers(data, conn, authproxy, request, path, TRUE); + if(conn->bits.origin_is_proxy || is_connect) { + result = output_auth_headers(data, conn, authproxy, request, + path_and_query, TRUE); if(result) - return result; + goto out; } else #else - (void)proxytunnel; + (void)is_connect; #endif /* CURL_DISABLE_PROXY */ /* we have no proxy so let's pretend we are done authenticating with it */ authproxy->done = TRUE; - /* To prevent the user+password to get sent to other than the original host - due to a location-follow */ - if(Curl_auth_allowed_to_host(data) -#ifndef CURL_DISABLE_NETRC - || conn->bits.netrc -#endif - ) - result = output_auth_headers(data, conn, authhost, request, path, FALSE); + /* Either we have credentials for the origin we talk to or + performing authentication is allowed here */ + if(data->state.creds || Curl_auth_allowed_to_host(data)) + result = output_auth_headers(data, conn, authhost, request, + path_and_query, FALSE); else authhost->done = TRUE; @@ -859,27 +858,31 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, else data->req.authneg = FALSE; +out: + curlx_free(tmp_str); return result; } -#else +#else /* !CURL_DISABLE_HTTP_AUTH */ /* when disabled */ CURLcode Curl_http_output_auth(struct Curl_easy *data, struct connectdata *conn, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel) + const char *query, + bool is_connect) { (void)data; (void)conn; (void)request; (void)httpreq; (void)path; - (void)proxytunnel; + (void)query; + (void)is_connect; return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_HTTP_AUTH, else */ #if defined(USE_SPNEGO) || defined(USE_NTLM) || \ !defined(CURL_DISABLE_DIGEST_AUTH) || \ @@ -1134,9 +1137,9 @@ static void http_switch_to_get(struct Curl_easy *data, int code) Curl_creader_set_rewind(data, FALSE); } -#define HTTPREQ_IS_POST(data) \ - ((data)->state.httpreq == HTTPREQ_POST || \ - (data)->state.httpreq == HTTPREQ_POST_FORM || \ +#define HTTPREQ_IS_POST(data) \ + ((data)->state.httpreq == HTTPREQ_POST || \ + (data)->state.httpreq == HTTPREQ_POST_FORM || \ (data)->state.httpreq == HTTPREQ_POST_MIME) CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, @@ -1227,8 +1230,6 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return CURLE_OUT_OF_MEMORY; } else { - bool same_origin; - CURLcode result; CURLU *u = curl_url(); if(!u) return CURLE_OUT_OF_MEMORY; @@ -1242,29 +1243,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return Curl_uc_to_curlcode(uc); } - same_origin = Curl_url_same_origin(u, data->state.uh); - curl_url_cleanup(u); - #ifndef CURL_DISABLE_DIGEST_AUTH - if(!same_origin) - Curl_auth_digest_cleanup(&data->state.digest); -#endif - - if((!same_origin && !data->set.allow_auth_to_other_hosts) || - !data->set.str[STRING_USERNAME]) { - result = Curl_reset_userpwd(data); - if(result) { - curlx_free(follow_url); - return result; - } - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); - } - result = Curl_reset_proxypwd(data); - if(result) { - curlx_free(follow_url); - return result; + { + bool same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); } +#else + curl_url_cleanup(u); +#endif } DEBUGASSERT(follow_url); @@ -1753,8 +1741,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, if(is_connect) proxy = HEADER_CONNECT; else - proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ? - HEADER_PROXY : HEADER_SERVER; + proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { case HEADER_SERVER: @@ -1773,6 +1760,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } #else (void)is_connect; @@ -1992,10 +1985,8 @@ static CURLcode http_useragent(struct Curl_easy *data) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent"))) { - curlx_free(data->state.aptr.uagent); - data->state.aptr.uagent = NULL; - } + if(Curl_checkheaders(data, STRCONST("User-Agent"))) + curlx_safefree(data->state.aptr.uagent); return CURLE_OK; } @@ -2005,25 +1996,15 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr; - if(!data->state.this_is_a_follow) { - /* Free to avoid leaking memory on multiple requests */ - curlx_free(data->state.first_host); - - data->state.first_host = curlx_strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->scheme->protocol; - } curlx_safefree(aptr->host); #ifndef CURL_DISABLE_COOKIES curlx_safefree(data->req.cookiehost); #endif ptr = Curl_checkheaders(data, STRCONST("Host")); - if(ptr && (!data->state.this_is_a_follow || - curl_strequal(data->state.first_host, conn->host.name))) { + if(ptr && + (!data->state.this_is_a_follow || + Curl_peer_equal(data->state.initial_origin, data->state.origin))) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2067,18 +2048,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) } else { /* Use the hostname as present in the URL if it was IPv6. */ - char *host = (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name; + char *host = (data->state.origin->user_hostname[0] == '[') ? + data->state.origin->user_hostname : data->state.origin->hostname; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && - (conn->remote_port == PORT_HTTPS)) || + (data->state.origin->port == PORT_HTTPS)) || ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && - (conn->remote_port == PORT_HTTP))) + (data->state.origin->port == PORT_HTTP))) /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ aptr->host = curl_maprintf("Host: %s\r\n", host); else - aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", + host, data->state.origin->port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -2106,7 +2088,7 @@ static CURLcode http_target(struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + if(conn->bits.origin_is_proxy) { /* Using a proxy but does not tunnel through it */ /* The path sent to the proxy is in fact the entire URL, but if the remote @@ -2120,8 +2102,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(conn->host.dispname != conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(data->state.origin->user_hostname != data->state.origin->hostname) { + uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; @@ -2146,6 +2128,19 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } + else if(data->state.creds && (data->state.creds->source != CREDS_URL)) { + /* credentials not from the URL need to be set */ + uc = curl_url_set(h, CURLUPART_USER, + data->state.creds->user, CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(h, CURLUPART_PASSWORD, + data->state.creds->passwd, CURLU_URLENCODE); + if(uc) { + curl_url_cleanup(h); + return Curl_uc_to_curlcode(uc); + } + } + /* Extract the URL to use in the request. */ uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); if(uc) { @@ -2541,7 +2536,8 @@ static CURLcode http_cookies(struct Curl_easy *data, char *addcookies = NULL; bool linecap = FALSE; if(data->set.str[STRING_COOKIE] && - !Curl_checkheaders(data, STRCONST("Cookie"))) + !Curl_checkheaders(data, STRCONST("Cookie")) && + Curl_auth_allowed_to_host(data)) addcookies = data->set.str[STRING_COOKIE]; if(data->cookies || addcookies) { @@ -2551,9 +2547,9 @@ static CURLcode http_cookies(struct Curl_easy *data, if(data->cookies && data->state.cookie_engine) { bool okay; const char *host = data->req.cookiehost ? - data->req.cookiehost : data->conn->host.name; + data->req.cookiehost : data->state.origin->hostname; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); + result = Curl_cookie_getlist(data, &okay, host, &list); if(!result && okay) { struct Curl_llist_node *n; size_t clen = 8; /* hold the size of the generated Cookie: header */ @@ -2737,13 +2733,16 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) alpn = Curl_conn_get_alpn_negotiated(data, conn); if(alpn && !strcmp("h3", alpn)) { - DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); +#ifndef CURL_DISABLE_PROXY + if(!conn->bits.origin_is_proxy) +#endif + DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); info_version = "HTTP/3"; } else if(alpn && !strcmp("h2", alpn)) { #ifndef CURL_DISABLE_PROXY if((Curl_conn_http_version(data, conn) != 20) && - conn->bits.proxy && !conn->bits.tunnel_proxy) { + conn->bits.origin_is_proxy) { result = Curl_http2_switch(data); if(result) return result; @@ -2842,7 +2841,7 @@ typedef enum { #ifndef CURL_DISABLE_PROXY H1_HD_PROXY_AUTH, #endif - H1_HD_USER_AUTH, + H1_HD_AUTH, H1_HD_RANGE, H1_HD_USER_AGENT, H1_HD_ACCEPT, @@ -2897,14 +2896,14 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->req.proxyuserpwd) - result = curlx_dyn_add(req, data->req.proxyuserpwd); + if(data->req.hd_proxy_auth) + result = curlx_dyn_add(req, data->req.hd_proxy_auth); break; #endif - case H1_HD_USER_AUTH: - if(data->req.userpwd) - result = curlx_dyn_add(req, data->req.userpwd); + case H1_HD_AUTH: + if(data->req.hd_auth) + result = curlx_dyn_add(req, data->req.hd_auth); break; case H1_HD_RANGE: @@ -2952,8 +2951,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_CONNECTION: - if(conn->bits.httpproxy && - !conn->bits.tunnel_proxy && + if(conn->bits.origin_is_proxy && !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection"))) result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); @@ -2966,10 +2964,10 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) + if(conn->bits.altused && conn->via_peer && + !Curl_checkheaders(data, STRCONST("Alt-Used"))) result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", - conn->conn_to_host.name, - conn->conn_to_port); + conn->via_peer->hostname, conn->via_peer->port); break; #endif @@ -3062,29 +3060,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* select host to send */ result = http_set_aptr_host(data); - if(!result) { - /* setup the authentication headers, how that method and host are known */ - char *pq = NULL; - if(data->state.up.query) { - pq = curl_maprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } + /* setup the authentication headers, how that method and host are known */ + if(!result) result = Curl_http_output_auth(data, data->conn, method, httpreq, - (pq ? pq : data->state.up.path), FALSE); - curlx_free(pq); - } - if(result) - goto out; - - result = http_useragent(data); - if(result) - goto out; - + data->state.up.path, + data->state.up.query, FALSE); + if(!result) + result = http_useragent(data); /* Setup input reader, resume information and ranges */ - result = set_reader(data, httpreq); + if(!result) + result = set_reader(data, httpreq); if(!result) result = http_resume(data, httpreq); if(!result) @@ -3209,7 +3194,6 @@ static CURLcode http_header_a(struct Curl_easy *data, { #ifndef CURL_DISABLE_ALTSVC const char *v; - struct connectdata *conn = data->conn; v = (data->asi && (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD @@ -3224,8 +3208,9 @@ static CURLcode http_header_a(struct Curl_easy *data, struct SingleRequest *k = &data->req; enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; - return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, - curlx_uitous((unsigned int)conn->remote_port)); + return Curl_altsvc_parse( + data, data->asi, v, id, data->state.origin->hostname, + curlx_uitous((unsigned int)data->state.origin->port)); } #else (void)data; @@ -3443,7 +3428,7 @@ static CURLcode http_header_p(struct Curl_easy *data, const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:"); if(v) { struct connectdata *conn = data->conn; - if((k->httpversion == 10) && conn->bits.httpproxy && + if((k->httpversion == 10) && conn->http_proxy.peer && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { /* * When an HTTP/1.0 reply comes when using a proxy, the @@ -3454,7 +3439,7 @@ static CURLcode http_header_p(struct Curl_easy *data, connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } - else if((k->httpversion == 11) && conn->bits.httpproxy && + else if((k->httpversion == 11) && conn->http_proxy.peer && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { /* * We get an HTTP/1.1 response from a proxy and it says it will @@ -3537,7 +3522,6 @@ static CURLcode http_header_s(struct Curl_easy *data, const char *hd, size_t hdlen) { #if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_HSTS) - struct connectdata *conn = data->conn; const char *v; #else (void)data; @@ -3552,8 +3536,8 @@ static CURLcode http_header_s(struct Curl_easy *data, /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ const char *host = data->req.cookiehost ? - data->req.cookiehost : conn->host.name; - const bool secure_context = Curl_secure_context(conn, host); + data->req.cookiehost : data->state.origin->hostname; + const bool secure_context = Curl_secure_context(data, host); CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, @@ -3575,8 +3559,8 @@ static CURLcode http_header_s(struct Curl_easy *data, ) ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; if(v) { - CURLcode result = - Curl_hsts_parse(data->hsts, conn->host.name, v); + CURLcode result = Curl_hsts_parse( + data->hsts, data->state.origin->hostname, v); if(result) { if(result == CURLE_OUT_OF_MEMORY) return result; @@ -3827,8 +3811,8 @@ static CURLcode http_size(struct Curl_easy *data) return CURLE_OK; } -static CURLcode verify_header(struct Curl_easy *data, - const char *hd, size_t hdlen) +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; const char *ptr = memchr(hd, 0x00, hdlen); @@ -3837,6 +3821,14 @@ static CURLcode verify_header(struct Curl_easy *data, failf(data, "Nul byte in header"); return CURLE_WEIRD_SERVER_REPLY; } + if(hdlen > 2) { + ptr = memchr(hd, '\r', hdlen - 2); + if(ptr) { + /* CR may only precede the LF, nothing else */ + failf(data, "Carriage return found in header"); + return CURLE_WEIRD_SERVER_REPLY; + } + } if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; @@ -4191,6 +4183,17 @@ static CURLcode http_on_response(struct Curl_easy *data, goto out; } + /* final response without error, prepare to receive the body */ + result = http_firstwrite(data); + if(result) + goto out; + + /* This is the last response that we get for the current request. Check on + * the body size and determine if the response is complete. */ + result = http_size(data); + if(result) + goto out; + /* If we requested a "no body", this is a good time to get * out and return home. */ @@ -4204,14 +4207,6 @@ static CURLcode http_on_response(struct Curl_easy *data, if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; - /* final response without error, prepare to receive the body */ - result = http_firstwrite(data); - - if(!result) - /* This is the last response that we get for the current request. Check on - * the body size and determine if the response is complete. */ - result = http_size(data); - out: if(last_hd) /* if not written yet, write it now */ @@ -4367,7 +4362,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, } } - result = verify_header(data, hd, hdlen); + result = Curl_verify_header(data, hd, hdlen); if(result) return result; @@ -4876,7 +4871,6 @@ struct name_const { size_t namelen; }; -/* keep them sorted by length! */ static const struct name_const H2_NON_FIELD[] = { { STRCONST("Host") }, { STRCONST("Upgrade") }, @@ -4890,10 +4884,8 @@ static bool h2_permissible_field(struct dynhds_entry *e) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) { - if(e->namelen < H2_NON_FIELD[i].namelen) - return TRUE; if(e->namelen == H2_NON_FIELD[i].namelen && - curl_strequal(H2_NON_FIELD[i].name, e->name)) + curl_strnequal(H2_NON_FIELD[i].name, e->name, e->namelen)) return FALSE; } return TRUE; @@ -4950,7 +4942,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = Curl_xfer_is_secure(data) ? "https" : "http"; + scheme = data->state.origin->scheme->name; } } diff --git a/deps/curl/lib/http.h b/deps/curl/lib/http.h index 6e33c00e92..5050215743 100644 --- a/deps/curl/lib/http.h +++ b/deps/curl/lib/http.h @@ -83,8 +83,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req); CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, int httpversion, struct dynbuf *req); -CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, - struct dynhds *hds); void Curl_http_to_fold(struct dynbuf *bf); @@ -108,6 +106,11 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, const char *hd, size_t hdlen, bool is_eos); +/* check a received header line for forbidden bytes/format, the same checks + applied to regular response headers */ +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen); + /* These functions are in http.c */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth); @@ -180,8 +183,9 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, * @param request pointer to the request keyword * @param httpreq is the request type * @param path pointer to the requested path - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" + * @param query pointer to the requested query or NULL + * @param is_connect boolean if this is a CONNECT request + * (where httpreq is HTTPREQ_GET since there is no HTTPREQ_CONNECT) * * @returns CURLcode */ @@ -190,9 +194,8 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel); /* TRUE if this is - the request setting up - the proxy tunnel */ + const char *query, + bool is_connect); /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); diff --git a/deps/curl/lib/http2.c b/deps/curl/lib/http2.c index 7be5abdd31..a820439ef4 100644 --- a/deps/curl/lib/http2.c +++ b/deps/curl/lib/http2.c @@ -47,14 +47,11 @@ #include "curlx/dynbuf.h" #include "headers.h" -#if (NGHTTP2_VERSION_NUM < 0x010c00) -#error too old nghttp2 version, upgrade! +#if NGHTTP2_VERSION_NUM < 0x010f00 +#error "nghttp2 1.15.0 or greater required" #endif -#if (NGHTTP2_VERSION_NUM >= 0x010c00) #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 -#endif - /* buffer dimensioning: * use 16K as chunk size, as that fits H2 DATA frames well */ @@ -189,6 +186,8 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) { if(ctx && ctx->initialized) { + if(ctx->h2) + nghttp2_session_del(ctx->h2); Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); Curl_bufcp_free(&ctx->stream_bufcp); @@ -199,14 +198,6 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) curlx_free(ctx); } -static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) -{ - if(ctx->h2) { - nghttp2_session_del(ctx->h2); - ctx->h2 = NULL; - } -} - static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) { #if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE @@ -221,8 +212,8 @@ static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) } static size_t populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data, - struct cf_h2_ctx *ctx) + struct Curl_easy *data, + struct cf_h2_ctx *ctx) { iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = Curl_multi_max_concurrent_streams(data->multi); @@ -232,7 +223,7 @@ static size_t populate_settings(nghttp2_settings_entry *iv, if(ctx) ctx->initial_win_size = iv[1].value; iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; + iv[2].value = !!data->multi->push_cb; return 3; } @@ -469,8 +460,7 @@ static int h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); @@ -859,7 +849,7 @@ static int push_promise(struct Curl_cfilter *cf, result = http2_data_setup(cf, newhandle, &newstream); if(result) { - failf(data, "error setting up stream: %d", result); + failf(data, "error setting up stream: %d", (int)result); discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; goto fail; @@ -908,7 +898,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, stream->xfer_result = cf_h2_update_local_win(cf, data, stream); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); } } @@ -925,7 +915,7 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, " "RST-ing stream", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE); } @@ -1389,7 +1379,7 @@ static void cf_h2_header_error(struct Curl_cfilter *cf, { struct cf_h2_ctx *ctx = cf->ctx; - failf(data, "Error receiving HTTP2 header: %d(%s)", result, + failf(data, "Error receiving HTTP2 header: %d(%s)", (int)result, curl_easy_strerror(result)); if(stream) { nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1409,7 +1399,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, struct Curl_cfilter *cf = userp; struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream; - struct Curl_easy *data_s; + struct Curl_easy *data; int32_t stream_id = frame->hd.stream_id; CURLcode result; (void)flags; @@ -1417,15 +1407,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ /* get the stream from the hash based on Stream ID */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!GOOD_EASY_HANDLE(data_s)) + data = nghttp2_session_get_stream_user_data(session, stream_id); + if(!GOOD_EASY_HANDLE(data)) /* Receiving a Stream ID not in the hash should not happen, this is an internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(ctx, data_s); + stream = H2_STREAM_CTX(ctx, data); if(!stream) { - failf(data_s, "Internal NULL stream"); + failf(data, "Internal NULL stream"); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1438,14 +1428,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { /* pseudo headers are lower case */ int rc = 0; - char *check = curl_maprintf("%s:%d", cf->conn->host.name, - cf->conn->remote_port); + char *check = curl_maprintf("%s:%d", data->state.origin->hostname, + data->state.origin->port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; if(!curl_strequal(check, (const char *)value) && - ((cf->conn->remote_port != cf->conn->given->defport) || - !curl_strequal(cf->conn->host.name, (const char *)value))) { + ((data->state.origin->port != cf->conn->given->defport) || + !curl_strequal(data->state.origin->hostname, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream @@ -1473,7 +1463,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, char **headp; if(stream->push_headers_alloc > 1000) { /* this is beyond crazy many headers, bail out */ - failf(data_s, "Too many PUSH_PROMISE headers"); + failf(data, "Too many PUSH_PROMISE headers"); free_push_headers(stream); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1497,13 +1487,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s", stream->id, (int)namelen, name, (int)valuelen, value); result = Curl_dynhds_add(&stream->resp_trailers, (const char *)name, namelen, (const char *)value, valuelen); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1511,21 +1501,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { /* nghttp2 guarantees :status is received first and only once. */ char buffer[32]; size_t hlen; result = Curl_http_decode_status(&stream->status_code, (const char *)value, valuelen); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r", stream->status_code); - result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); + result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } curlx_dyn_reset(&ctx->scratch); @@ -1535,17 +1525,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!result) result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); if(!result) - h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), + h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), curlx_dyn_len(&ctx->scratch), FALSE); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) - Curl_multi_mark_dirty(data_s); + if(CF_DATA_CURRENT(cf) != data) + Curl_multi_mark_dirty(data); - CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", + CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", stream->id, stream->status_code); return 0; } @@ -1562,17 +1552,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!result) result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); if(!result) - h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), + h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), curlx_dyn_len(&ctx->scratch), FALSE); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) - Curl_multi_mark_dirty(data_s); + if(CF_DATA_CURRENT(cf) != data) + Curl_multi_mark_dirty(data); - CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", stream->id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ @@ -1620,7 +1610,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session, nread = (ssize_t)n; CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d", - stream_id, length, stream->body_eos, nread, result); + stream_id, length, stream->body_eos, nread, (int)result); if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) { *data_flags = NGHTTP2_DATA_FLAG_EOF; @@ -1701,12 +1691,12 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } else if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " - "not want a body anyway, ignore: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - stream->close_handled = TRUE; - return CURLE_OK; + CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " + "not want a body anyway, ignore: %s (err %u)", + stream->id, nghttp2_http2_strerror(stream->error), + stream->error); + stream->close_handled = TRUE; + return CURLE_OK; } failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", stream->id, stream->reset_by_server ? "server" : "curl", @@ -1754,7 +1744,7 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen); + CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", (int)result, *pnlen); return result; } @@ -1778,16 +1768,12 @@ static int sweight_in_effect(const struct Curl_easy *data) * struct. */ -static void h2_pri_spec(struct cf_h2_ctx *ctx, - struct Curl_easy *data, +static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { struct Curl_data_priority *prio = &data->set.priority; - struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); - int32_t depstream_id = depstream ? depstream->id : 0; - nghttp2_priority_spec_init(pri_spec, depstream_id, - sweight_wanted(data), - data->set.priority.exclusive); + nghttp2_priority_spec_init(pri_spec, 0, + sweight_wanted(data), FALSE); data->state.priority = *prio; } @@ -1805,13 +1791,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, int rv = 0; if(stream && stream->id > 0 && - ((sweight_wanted(data) != sweight_in_effect(data)) || - (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent))) { + (sweight_wanted(data) != sweight_in_effect(data))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1869,7 +1853,7 @@ static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result && (result != CURLE_AGAIN)) CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu", - stream->id, len, result, *pnread); + stream->id, len, (int)result, *pnread); return result; } @@ -1920,7 +1904,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(result) { if(result != CURLE_AGAIN) { - failf(data, "Failed receiving HTTP2 data: %d(%s)", result, + failf(data, "Failed receiving HTTP2 data: %d(%s)", (int)result, curl_easy_strerror(result)); return result; } @@ -2015,7 +1999,7 @@ static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, " "window=%d/%d, connection %d/%d", - stream->id, len, result, *pnread, + stream->id, len, (int)result, *pnread, nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -2123,7 +2107,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, goto out; } - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); @@ -2200,7 +2184,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, out: CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", - stream ? stream->id : -1, result, *pnwritten); + stream ? stream->id : -1, (int)result, *pnwritten); curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); @@ -2234,7 +2218,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(eos); result = cf_h2_body_send(cf, data, stream, buf, 0, eos, &n); CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %d, %zu, eos=%d", - stream->id, result, n, eos); + stream->id, (int)result, n, eos); if(result) goto out; *pnwritten = len; @@ -2242,7 +2226,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { result = cf_h2_body_send(cf, data, stream, buf, len, eos, pnwritten); CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %d, %zu, eos=%d", - stream->id, len, result, *pnwritten, eos); + stream->id, len, (int)result, *pnwritten, eos); } /* Call the nghttp2 send loop and flush to write ALL buffered data, @@ -2278,7 +2262,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "eos=%d, h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, len, result, *pnwritten, + stream->id, len, (int)result, *pnwritten, stream->body_eos, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), @@ -2289,7 +2273,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, " "connection-window=%d, nw_send_buffer(%zu)", - len, result, *pnwritten, + len, (int)result, *pnwritten, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } @@ -2322,7 +2306,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, result, + stream->id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -2332,7 +2316,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, else { CURL_TRC_CF(data, cf, "flush -> %d, " "connection-window=%d, nw_send_buffer(%zu)", - result, nghttp2_session_get_remote_window_size(ctx->h2), + (int)result, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); @@ -2551,27 +2535,11 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } -static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_ctx_close(ctx); - CF_DATA_RESTORE(cf, save); - cf->connected = FALSE; - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; @@ -2794,7 +2762,6 @@ struct Curl_cftype Curl_cft_nghttp2 = { CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, - cf_h2_close, cf_h2_shutdown, cf_h2_adjust_pollset, cf_h2_data_pending, @@ -2869,9 +2836,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data) (data->state.http_neg.wanted & CURL_HTTP_V2x) && data->state.http_neg.h2_prior_knowledge) { #ifndef CURL_DISABLE_PROXY - if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { - /* We do not support HTTP/2 proxies yet. Also it is debatable - whether or not this setting should apply to HTTP/2 proxies. */ + if(data->conn->bits.origin_is_proxy) { infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); return FALSE; } @@ -2951,7 +2916,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, result = Curl_bufq_write(&ctx->inbufq, (const unsigned char *)mem, nread, &copied); if(result) { - failf(data, "error on copying HTTP Upgrade response: %d", result); + failf(data, "error on copying HTTP Upgrade response: %d", (int)result); return CURLE_RECV_ERROR; } if(copied < nread) { diff --git a/deps/curl/lib/http_aws_sigv4.c b/deps/curl/lib/http_aws_sigv4.c index 55efa12047..780eed67b9 100644 --- a/deps/curl/lib/http_aws_sigv4.c +++ b/deps/curl/lib/http_aws_sigv4.c @@ -271,13 +271,12 @@ static bool should_urlencode(struct Curl_str *service_name) * should_urlencode == true is equivalent to should_urlencode_uri_path * from the AWS SDK. Urls are already normalized by the curl URL parser */ - if(curlx_str_cmp(service_name, "s3") || curlx_str_cmp(service_name, "s3-express") || curlx_str_cmp(service_name, "s3-outposts")) { - return false; + return FALSE; } - return true; + return TRUE; } /* maximum length for the aws sivg4 parts */ @@ -384,7 +383,7 @@ static CURLcode make_headers(struct Curl_easy *data, char date_full_hdr[DATE_FULL_HDR_LEN]; struct curl_slist *head = NULL; struct curl_slist *tmp_head = NULL; - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct curl_slist *l; bool again = TRUE; @@ -516,8 +515,8 @@ static CURLcode make_headers(struct Curl_easy *data, } } while(again); - ret = merge_duplicate_headers(head); - if(ret) + result = merge_duplicate_headers(head); + if(result) goto fail; for(l = head; l; l = l->next) { @@ -540,11 +539,11 @@ static CURLcode make_headers(struct Curl_easy *data, goto fail; } - ret = CURLE_OK; + result = CURLE_OK; fail: curl_slist_free_all(head); - return ret; + return result; } #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) @@ -618,12 +617,12 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, bool empty_payload = (empty_method || data->set.filesize == 0); /* The POST payload is in memory */ bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; if(empty_payload || post_payload) { /* Calculate a real hash when we know the request payload */ - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; } else { @@ -638,9 +637,9 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, curl_msnprintf(header, CONTENT_SHA256_HDR_LEN, "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); - ret = CURLE_OK; + result = CURLE_OK; fail: - return ret; + return result; } static int compare_func(const void *a, const void *b) @@ -818,66 +817,14 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) return result; } -CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +static CURLcode parse_sigv4_params(struct Curl_easy *data, + const char *hostname, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *region, + struct Curl_str *service) { - CURLcode result = CURLE_OUT_OF_MEMORY; - struct connectdata *conn = data->conn; - const char *line; - struct Curl_str provider0; - struct Curl_str provider1; - struct Curl_str region = { NULL, 0 }; - struct Curl_str service = { NULL, 0 }; - const char *hostname = conn->host.name; - time_t clock; - struct tm tm; - char timestamp[TIMESTAMP_SIZE]; - char date[9]; - struct dynbuf canonical_headers; - struct dynbuf signed_headers; - struct dynbuf canonical_query; - struct dynbuf canonical_path; - char *date_header = NULL; - Curl_HttpReq httpreq; - const char *method = NULL; - const char *payload_hash = NULL; - size_t payload_hash_len = 0; - unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; - char sha_hex[SHA256_HEX_LENGTH]; - char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ - char *canonical_request = NULL; - char *request_type = NULL; - char *credential_scope = NULL; - char *str_to_sign = NULL; - const char *user = data->state.aptr.user ? data->state.aptr.user : ""; - char *secret = NULL; - unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - char *auth_headers = NULL; - - if(data->set.path_as_is) { - failf(data, "Cannot use sigv4 authentication with path-as-is flag"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - if(Curl_checkheaders(data, STRCONST("Authorization"))) { - /* Authorization already present, Bailing out */ - return CURLE_OK; - } - - /* we init those buffers here, so goto fail will free initialized dynbuf */ - curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); - - /* - * Parameters parsing - * Google and Outscale use the same OSC or GOOG, - * but Amazon uses AWS and AMZ for header arguments. - * AWS is the default because most of non-amazon providers - * are still using aws:amz as a prefix. - */ - line = data->set.str[STRING_AWS_SIGV4]; + const char *line = data->set.str[STRING_AWS_SIGV4]; if(!line || !*line) line = "aws:amz"; @@ -885,71 +832,89 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) No string can be longer than N bytes of non-whitespace */ - if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) { + if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) { failf(data, "first aws-sigv4 provider cannot be empty"); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto fail; + return CURLE_BAD_FUNCTION_ARGUMENT; } if(curlx_str_single(&line, ':') || - curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) { - provider1 = provider0; + curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) { + *provider1 = *provider0; } else if(curlx_str_single(&line, ':') || - curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') || + curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') || curlx_str_single(&line, ':') || - curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) { + curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) { /* nothing to do */ } - if(!curlx_strlen(&service)) { + if(!curlx_strlen(service)) { const char *p = hostname; - if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') || + if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: service missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked service %.*s from host", - (int)curlx_strlen(&service), curlx_str(&service)); + (int)curlx_strlen(service), curlx_str(service)); - if(!curlx_strlen(®ion)) { - if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') || + if(!curlx_strlen(region)) { + if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: region missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked region %.*s from host", - (int)curlx_strlen(®ion), curlx_str(®ion)); + (int)curlx_strlen(region), curlx_str(region)); } } - Curl_http_method(data, &method, &httpreq); + return CURLE_OK; +} - payload_hash = - parse_content_sha_hdr(data, curlx_str(&provider1), - curlx_strlen(&provider1), &payload_hash_len); +static CURLcode get_payload_hash(struct Curl_easy *data, + Curl_HttpReq httpreq, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *service, + unsigned char *sha_hash, + char *sha_hex, + char *content_sha256_hdr, + const char **payload_hash_out, + size_t *payload_hash_len_out) +{ + *payload_hash_out = + parse_content_sha_hdr(data, curlx_str(provider1), + curlx_strlen(provider1), payload_hash_len_out); - if(!payload_hash) { + if(!*payload_hash_out) { + CURLcode result; /* AWS S3 requires a x-amz-content-sha256 header, and supports special * values like UNSIGNED-PAYLOAD */ - bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") && - curlx_str_casecompare(&service, "s3"); + bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") && + curlx_str_casecompare(service, "s3"); if(sign_as_s3) - result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1), - curlx_strlen(&provider1), sha_hash, + result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1), + curlx_strlen(provider1), sha_hash, sha_hex, content_sha256_hdr); else result = calc_payload_hash(data, sha_hash, sha_hex); if(result) - goto fail; + return result; - payload_hash = sha_hex; + *payload_hash_out = sha_hex; /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ - payload_hash_len = strlen(sha_hex); + *payload_hash_len_out = strlen(sha_hex); } + return CURLE_OK; +} + +static CURLcode get_timestamp(char *timestamp, size_t stampsize) +{ + time_t clock; + struct tm tm; + CURLcode result; #ifdef DEBUGBUILD { @@ -963,43 +928,54 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) clock = time(NULL); #endif result = curlx_gmtime(clock, &tm); - if(result) { - goto fail; - } - if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + if(result) + return result; + + if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static CURLcode make_canonical_request(struct Curl_easy *data, + const char *hostname, + char *timestamp, + struct Curl_str *provider1, + struct Curl_str *service, + const char *method, + const char *payload_hash, + size_t payload_hash_len, + char **date_header_out, + char *content_sha256_hdr, + struct dynbuf *canonical_headers, + struct dynbuf *signed_headers, + char **canonical_request_out) +{ + struct dynbuf canonical_query; + struct dynbuf canonical_path; + CURLcode result; + + curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); result = make_headers(data, hostname, timestamp, - curlx_str(&provider1), curlx_strlen(&provider1), - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); + curlx_str(provider1), curlx_strlen(provider1), + date_header_out, content_sha256_hdr, + canonical_headers, signed_headers); if(result) goto fail; - if(*content_sha256_hdr) { - /* make_headers() needed this without the \r\n for canonicalization */ - size_t hdrlen = strlen(content_sha256_hdr); - DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); - memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); - } - - memcpy(date, timestamp, sizeof(date)); - date[sizeof(date) - 1] = 0; - result = canon_query(data->state.up.query, &canonical_query); if(result) goto fail; result = canon_path(data->state.up.path, strlen(data->state.up.path), &canonical_path, - should_urlencode(&service)); + should_urlencode(service)); if(result) goto fail; - result = CURLE_OUT_OF_MEMORY; - canonical_request = + *canonical_request_out = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ "%s\n" /* CanonicalQueryString */ @@ -1010,37 +986,65 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) curlx_dyn_ptr(&canonical_path), curlx_dyn_ptr(&canonical_query) ? curlx_dyn_ptr(&canonical_query) : "", - curlx_dyn_ptr(&canonical_headers), - curlx_dyn_ptr(&signed_headers), + curlx_dyn_ptr(canonical_headers), + curlx_dyn_ptr(signed_headers), (int)payload_hash_len, payload_hash); - if(!canonical_request) + if(!*canonical_request_out) { + result = CURLE_OUT_OF_MEMORY; goto fail; + } + + result = CURLE_OK; +fail: + curlx_dyn_free(&canonical_query); + curlx_dyn_free(&canonical_path); + return result; +} - infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]", - canonical_request); +static CURLcode make_string_to_sign(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *date, + const char *timestamp, + const char *canonical_request, + char **request_type_out, + char **credential_scope_out, + char **str_to_sign_out) +{ + char *request_type; + char *credential_scope; + char *str_to_sign; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; request_type = curl_maprintf("%.*s4_request", - (int)curlx_strlen(&provider0), - curlx_str(&provider0)); + (int)curlx_strlen(provider0), + curlx_str(provider0)); if(!request_type) - goto fail; + return CURLE_OUT_OF_MEMORY; /* provider0 is lowercased *after* curl_maprintf() so that the buffer can be written to */ - Curl_strntolower(request_type, request_type, curlx_strlen(&provider0)); + Curl_strntolower(request_type, request_type, curlx_strlen(provider0)); credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date, - (int)curlx_strlen(®ion), - curlx_str(®ion), - (int)curlx_strlen(&service), - curlx_str(&service), + (int)curlx_strlen(region), + curlx_str(region), + (int)curlx_strlen(service), + curlx_str(service), request_type); - if(!credential_scope) - goto fail; + if(!credential_scope) { + curlx_free(request_type); + return CURLE_OUT_OF_MEMORY; + } - if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request, - strlen(canonical_request))) - goto fail; + if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request, + strlen(canonical_request))) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } sha256_to_hex(sha_hex, sha_hash); @@ -1052,36 +1056,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "%s\n" /* RequestDateTime */ "%s\n" /* CredentialScope */ "%s", /* HashedCanonicalRequest in hex */ - (int)curlx_strlen(&provider0), - curlx_str(&provider0), + (int)curlx_strlen(provider0), + curlx_str(provider0), timestamp, credential_scope, sha_hex); - if(!str_to_sign) - goto fail; + if(!str_to_sign) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } /* make provider0 part done uppercase */ - Curl_strntoupper(str_to_sign, curlx_str(&provider0), - curlx_strlen(&provider0)); + Curl_strntoupper(str_to_sign, curlx_str(provider0), + curlx_strlen(provider0)); infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]", str_to_sign); - secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + *request_type_out = request_type; + *credential_scope_out = credential_scope; + *str_to_sign_out = str_to_sign; + return CURLE_OK; +} + +static CURLcode sign_and_set_auth_headers(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *request_type, + const char *credential_scope, + const char *date, + const char *str_to_sign, + const char *date_header, + const char *content_sha256_hdr, + struct dynbuf *signed_headers) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + const char *passwd = Curl_creds_passwd(data->state.creds); + char *secret = NULL; + unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + char sha_hex[SHA256_HEX_LENGTH]; + char *auth_headers = NULL; + char *user = curl_escape(Curl_creds_user(data->state.creds), 0); + if(!user) + return CURLE_OUT_OF_MEMORY; + + secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0), + curlx_str(provider0), passwd); if(!secret) goto fail; /* make provider0 part done uppercase */ - Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0)); + Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0)); HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); HMAC_SHA256(sign0, sizeof(sign0), - curlx_str(®ion), curlx_strlen(®ion), sign1); + curlx_str(region), curlx_strlen(region), sign1); HMAC_SHA256(sign1, sizeof(sign1), - curlx_str(&service), curlx_strlen(&service), sign0); - HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); - HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); + curlx_str(service), curlx_strlen(service), sign0); + HMAC_SHA256(sign0, sizeof(sign0), + request_type, strlen(request_type), sign1); + HMAC_SHA256(sign1, sizeof(sign1), + str_to_sign, strlen(str_to_sign), sign0); sha256_to_hex(sha_hex, sign0); @@ -1091,43 +1128,119 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" + "%s" + "%s%s", + (int)curlx_strlen(provider0), + curlx_str(provider0), + user, + credential_scope, + curlx_dyn_ptr(signed_headers), + sha_hex, /* * date_header is added here, only if it was not * user-specified (using CURLOPT_HTTPHEADER). * date_header includes \r\n */ - "%s" - "%s", /* optional sha256 header includes \r\n */ - (int)curlx_strlen(&provider0), - curlx_str(&provider0), - user, - credential_scope, - curlx_dyn_ptr(&signed_headers), - sha_hex, date_header ? date_header : "", - content_sha256_hdr); - if(!auth_headers) { + content_sha256_hdr, + content_sha256_hdr[0] ? "\r\n": ""); + if(!auth_headers) goto fail; - } + /* provider 0 uppercase */ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], - curlx_str(&provider0), curlx_strlen(&provider0)); + curlx_str(provider0), curlx_strlen(provider0)); - curlx_free(data->req.userpwd); - data->req.userpwd = auth_headers; + curlx_free(data->req.hd_auth); + data->req.hd_auth = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK; fail: - curlx_dyn_free(&canonical_query); - curlx_dyn_free(&canonical_path); + curlx_free(user); + curlx_free(secret); + return result; +} + +CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + struct Curl_str provider0 = { NULL, 0 }; + struct Curl_str provider1 = { NULL, 0 }; + struct Curl_str region = { NULL, 0 }; + struct Curl_str service = { NULL, 0 }; + const char *hostname = data->state.origin->hostname; + char timestamp[TIMESTAMP_SIZE]; + char date[9]; + struct dynbuf canonical_headers; + struct dynbuf signed_headers; + char *date_header = NULL; + Curl_HttpReq httpreq; + const char *method = NULL; + const char *payload_hash = NULL; + size_t payload_hash_len = 0; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; + char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ + char *canonical_request = NULL; + char *request_type = NULL; + char *credential_scope = NULL; + char *str_to_sign = NULL; + + if(data->set.path_as_is) { + failf(data, "Cannot use sigv4 authentication with path-as-is flag"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(Curl_checkheaders(data, STRCONST("Authorization"))) + /* Authorization already present, Bailing out */ + return CURLE_OK; + + /* we init those buffers here, so goto fail will free initialized dynbuf */ + curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); + + result = parse_sigv4_params(data, hostname, &provider0, &provider1, + ®ion, &service); + if(!result) { + Curl_http_method(data, &method, &httpreq); + result = get_payload_hash(data, httpreq, &provider0, &provider1, &service, + sha_hash, sha_hex, content_sha256_hdr, + &payload_hash, &payload_hash_len); + } + + if(!result) + result = get_timestamp(timestamp, sizeof(timestamp)); + + if(!result) + result = make_canonical_request(data, hostname, timestamp, + &provider1, &service, + method, payload_hash, payload_hash_len, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers, + &canonical_request); + if(!result) { + /* the timestamp might have been updated in make_canonical_request */ + memcpy(date, timestamp, sizeof(date) - 1); + date[sizeof(date) - 1] = 0; + + result = make_string_to_sign(data, &provider0, ®ion, &service, + date, timestamp, canonical_request, + &request_type, &credential_scope, + &str_to_sign); + } + if(!result) + result = sign_and_set_auth_headers(data, &provider0, ®ion, &service, + request_type, credential_scope, + date, str_to_sign, date_header, + content_sha256_hdr, &signed_headers); + curlx_dyn_free(&canonical_headers); curlx_dyn_free(&signed_headers); curlx_free(canonical_request); curlx_free(request_type); curlx_free(credential_scope); curlx_free(str_to_sign); - curlx_free(secret); curlx_free(date_header); return result; } diff --git a/deps/curl/lib/http_chunks.c b/deps/curl/lib/http_chunks.c index aa45c79e38..9596fc6693 100644 --- a/deps/curl/lib/http_chunks.c +++ b/deps/curl/lib/http_chunks.c @@ -27,6 +27,7 @@ #include "urldata.h" /* it includes http_chunks.h */ #include "curl_trc.h" +#include "http.h" /* for Curl_verify_header */ #include "sendf.h" /* for the client write stuff */ #include "curlx/dynbuf.h" #include "multiif.h" @@ -153,7 +154,8 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, if(ch->hexindex == 0) { /* This is illegal data, we received junk where we expected a hexadecimal digit. */ - failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf); + failf(data, "chunk hex-length char not a hex digit: 0x%x", + (unsigned int)*buf); ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_ILLEGAL_HEX; return CURLE_RECV_ERROR; @@ -247,6 +249,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, there was no trailer and we move on */ if(tr) { + size_t trlen; result = curlx_dyn_addn(&ch->trailer, STRCONST("\x0d\x0a")); if(result) { ch->state = CHUNK_FAILED; @@ -254,8 +257,18 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, return result; } tr = curlx_dyn_ptr(&ch->trailer); + trlen = curlx_dyn_len(&ch->trailer); + + /* a trailer is delivered to the client as a header, so it must pass + the same checks as a regular response header */ + result = Curl_verify_header(data, tr, trlen); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + return result; + } + if(!data->set.http_te_skip) { - size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_HEADER | @@ -535,7 +548,7 @@ static CURLcode add_last_chunk(struct Curl_easy *data, out: curl_slist_free_all(trailers); CURL_TRC_READ(data, "http_chunk, added last chunk with trailers " - "from client -> %d", result); + "from client -> %d", (int)result); return result; } @@ -583,7 +596,7 @@ static CURLcode add_chunk(struct Curl_easy *data, if(!result) result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n); CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d", - nread, result); + nread, (int)result); if(result) return result; } diff --git a/deps/curl/lib/http_digest.c b/deps/curl/lib/http_digest.c index b7007071e7..25783d0969 100644 --- a/deps/curl/lib/http_digest.c +++ b/deps/curl/lib/http_digest.c @@ -54,7 +54,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data, } if(!checkprefix("Digest", header) || !ISBLANK(header[6])) - return CURLE_BAD_CONTENT_ENCODING; + return CURLE_AUTH_ERROR; header += strlen("Digest"); curlx_str_passblanks(&header); @@ -62,14 +62,33 @@ CURLcode Curl_input_digest(struct Curl_easy *data, return Curl_auth_decode_digest_http_message(header, digest); } +/* Flush the Digest state if it was created for a different origin or with + different credentials than the ones now in use, then link the current + ones. */ +static void digest_flush_stale(struct digestdata *digest, + struct Curl_peer *peer, + struct Curl_creds *creds) +{ + bool flush = FALSE; + if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) + flush = TRUE; + else if(digest->creds && !Curl_creds_same(creds, digest->creds)) + flush = TRUE; + + if(flush) + /* flush Digest state */ + Curl_auth_digest_cleanup(digest); + + Curl_peer_link(&digest->origin, peer); + Curl_creds_link(&digest->creds, creds); +} + CURLcode Curl_output_digest(struct Curl_easy *data, bool proxy, const unsigned char *request, const unsigned char *uripath) { CURLcode result; - unsigned char *path = NULL; - const char *tmp = NULL; char *response; size_t len; bool have_chlg; @@ -79,8 +98,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, char **allocuserpwd; /* Point to the name and password for this */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; /* Point to the correct struct with this */ struct digestdata *digest; @@ -91,29 +109,24 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->req.proxyuserpwd; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + digest_flush_stale(digest, data->conn->http_proxy.peer, + data->conn->http_proxy.creds); + allocuserpwd = &data->req.hd_proxy_auth; + creds = data->conn->http_proxy.creds; authp = &data->state.authproxy; #endif } else { + DEBUGASSERT(data->state.origin); digest = &data->state.digest; - allocuserpwd = &data->req.userpwd; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + digest_flush_stale(digest, data->state.origin, data->state.creds); + allocuserpwd = &data->req.hd_auth; + creds = data->state.creds; authp = &data->state.authhost; } curlx_safefree(*allocuserpwd); - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI have_chlg = !!digest->input_token; #else @@ -125,36 +138,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - /* IE browsers < v7 cut off the URI part at the query part when they - evaluate the MD5 and some (IIS?) servers work with them so we may need to - do the Digest IE-style. Note that the different ways cause different MD5 - sums to get sent. - - Apache servers can be set to do the Digest IE-style automatically using - the BrowserMatch feature: - https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie - - Further details on Digest implementation differences: - https://web.archive.org/web/2009/fngtps.com/2006/09/http-authentication - */ - - if(authp->iestyle) { - tmp = strchr((const char *)uripath, '?'); - if(tmp) { - size_t urilen = tmp - (const char *)uripath; - /* typecast is fine here since the value is always less than 32 bits */ - path = (unsigned char *)curl_maprintf("%.*s", (int)urilen, uripath); - } - } - if(!tmp) - path = (unsigned char *)curlx_strdup((const char *)uripath); - - if(!path) - return CURLE_OUT_OF_MEMORY; - - result = Curl_auth_create_digest_http_message(data, userp, passwdp, request, - path, digest, &response, &len); - curlx_free(path); + result = Curl_auth_create_digest_http_message(data, creds, request, + uripath, digest, + &response, &len); if(result) return result; diff --git a/deps/curl/lib/http_negotiate.c b/deps/curl/lib/http_negotiate.c index 74d63d6cc0..891369b5bc 100644 --- a/deps/curl/lib/http_negotiate.c +++ b/deps/curl/lib/http_negotiate.c @@ -40,8 +40,11 @@ static void http_auth_nego_reset(struct connectdata *conn, { if(proxy) conn->proxy_negotiate_state = GSS_AUTHNONE; - else + else { conn->http_negotiate_state = GSS_AUTHNONE; + Curl_peer_unlink(&conn->creds_origin); + Curl_creds_unlink(&conn->creds); + } if(neg_ctx) Curl_auth_cleanup_spnego(neg_ctx); } @@ -52,10 +55,8 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, CURLcode result; size_t len; - /* Point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service; + /* Point to credentials and host */ + struct Curl_creds *creds = NULL; const char *host; /* Point to the correct struct with this */ @@ -64,22 +65,16 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = conn->http_proxy.user; - passwdp = conn->http_proxy.passwd; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - host = conn->http_proxy.host.name; + creds = conn->http_proxy.creds; + host = conn->http_proxy.peer->hostname; state = conn->proxy_negotiate_state; #else return CURLE_NOT_BUILT_IN; #endif } else { - userp = conn->user; - passwdp = conn->passwd; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; - host = conn->host.name; + creds = data->state.creds; + host = data->state.origin->hostname; state = conn->http_negotiate_state; } @@ -87,13 +82,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(!neg_ctx) return CURLE_OUT_OF_MEMORY; - /* Not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - /* Obtain the input token, if any */ header += strlen("Negotiate"); curlx_str_passblanks(&header); @@ -135,7 +123,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, + result = Curl_auth_decode_spnego_message(data, creds, "HTTP", host, header, neg_ctx); #ifdef GSS_C_CHANNEL_BOUND_FLAG @@ -145,6 +133,22 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(result) http_auth_nego_reset(conn, neg_ctx, proxy); + if(!result && !proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, creds); + } + return result; } @@ -217,13 +221,13 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(proxy) { #ifndef CURL_DISABLE_PROXY - curlx_free(data->req.proxyuserpwd); - data->req.proxyuserpwd = userp; + curlx_free(data->req.hd_proxy_auth); + data->req.hd_proxy_auth = userp; #endif } else { - curlx_free(data->req.userpwd); - data->req.userpwd = userp; + curlx_free(data->req.hd_auth); + data->req.hd_auth = userp; } curlx_free(base64); diff --git a/deps/curl/lib/http_ntlm.c b/deps/curl/lib/http_ntlm.c index 82b050529e..dc9911fdac 100644 --- a/deps/curl/lib/http_ntlm.c +++ b/deps/curl/lib/http_ntlm.c @@ -93,6 +93,8 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, else if(*state == NTLMSTATE_TYPE3) { infof(data, "NTLM handshake rejected"); Curl_auth_ntlm_remove(conn, proxy); + Curl_peer_unlink(&conn->creds_origin); + Curl_creds_unlink(&conn->creds); *state = NTLMSTATE_NONE; return CURLE_REMOTE_ACCESS_DENIED; } @@ -122,10 +124,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) server, which is for a plain host or for an HTTP proxy */ char **allocuserpwd; - /* point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service = NULL; + /* point to credentials and host */ + struct Curl_creds *creds = NULL; const char *hostname = NULL; /* point to the correct struct with this */ @@ -139,12 +139,9 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->req.proxyuserpwd; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - hostname = conn->http_proxy.host.name; + allocuserpwd = &data->req.hd_proxy_auth; + creds = conn->http_proxy.creds; + hostname = conn->http_proxy.peer->hostname; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; #else @@ -152,27 +149,18 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #endif } else { - allocuserpwd = &data->req.userpwd; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; - hostname = conn->host.name; + allocuserpwd = &data->req.hd_auth; + creds = data->state.creds; + hostname = data->state.origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; } + ntlm = Curl_auth_ntlm_get(conn, proxy); if(!ntlm) return CURLE_OUT_OF_MEMORY; authp->done = FALSE; - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ @@ -194,9 +182,23 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) switch(*state) { case NTLMSTATE_TYPE1: - default: /* for the weird cases we (re)start here */ - /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, + default: /* for the weird cases we (re)start here */ + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, creds); + } + result = Curl_auth_create_ntlm_type1_message(data, creds, "HTTP", hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); @@ -215,8 +217,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ - result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, - ntlm, &ntlmmsg); + result = Curl_auth_create_ntlm_type3_message(data, creds, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); diff --git a/deps/curl/lib/http_proxy.c b/deps/curl/lib/http_proxy.c index a4bdd7e361..d019796b53 100644 --- a/deps/curl/lib/http_proxy.c +++ b/deps/curl/lib/http_proxy.c @@ -35,11 +35,12 @@ #include "cf-h2-proxy.h" #include "connect.h" #include "vauth/vauth.h" +#include "vquic/vquic.h" #include "curlx/strparse.h" static CURLcode dynhds_add_custom(struct Curl_easy *data, bool is_connect, int httpversion, - struct dynhds *hds) + bool is_udp, struct dynhds *hds) { struct connectdata *conn = data->conn; struct curl_slist *h[2]; @@ -49,11 +50,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, enum Curl_proxy_use proxy; - if(is_connect) + if(is_connect && !is_udp) proxy = HEADER_CONNECT; + else if(is_connect && is_udp) + proxy = HEADER_CONNECT_UDP; else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ? - HEADER_PROXY : HEADER_SERVER; + proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { case HEADER_SERVER: @@ -72,6 +74,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } /* loop through one or two lists */ @@ -162,52 +170,43 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, return CURLE_OK; } -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip) -{ - DEBUGASSERT(cf); - DEBUGASSERT(cf->conn); - - if(cf->conn->bits.conn_to_host) - *phostname = cf->conn->conn_to_host.name; - else if(cf->sockindex == SECONDARYSOCKET) - *phostname = cf->conn->secondaryhostname; - else - *phostname = cf->conn->host.name; - - if(cf->sockindex == SECONDARYSOCKET) - *pport = cf->conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - *pport = cf->conn->conn_to_port; - else - *pport = cf->conn->remote_port; - - *pipv6_ip = (strchr(*phostname, ':') != NULL); -} - struct cf_proxy_ctx { - int httpversion; /* HTTP version used to CONNECT */ + struct Curl_peer *peer; /* proxy */ + struct Curl_peer *tunnel_peer; /* tunnel destination */ + uint8_t proxytype; + uint8_t tunnel_transport; BIT(sub_filter_installed); }; -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - int http_version_major) +static int proxy_http_ver_major(proxy_http_ver ver) +{ + switch(ver) { + case PROXY_HTTP_V1: + return 11; + case PROXY_HTTP_V2: + return 20; + case PROXY_HTTP_V3: + return 30; + } + return 0; +} + +static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) { - struct cf_proxy_ctx *ctx = cf->ctx; - const char *hostname = NULL; char *authority = NULL; - uint16_t port; - bool ipv6_ip; + int httpversion = proxy_http_ver_major(ver); CURLcode result; struct httpreq *req = NULL; - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - - authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + authority = curl_maprintf("%s%s%s:%u", + dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!authority) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -221,21 +220,21 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, - req->authority, TRUE); + req->authority, NULL, TRUE); if(result) goto out; /* If user is not overriding Host: header, we add for HTTP/1.x */ - if(http_version_major == 1 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { result = Curl_dynhds_cadd(&req->headers, "Host", authority); if(result) goto out; } - if(data->req.proxyuserpwd) { + if(data->req.hd_proxy_auth) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->req.proxyuserpwd); + data->req.hd_proxy_auth); if(result) goto out; } @@ -248,14 +247,15 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(http_version_major == 1 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; } - result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); + result = dynhds_add_custom(data, TRUE, httpversion, + FALSE, &req->headers); out: if(result && req) { @@ -267,66 +267,385 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, return result; } +static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) +{ + const char *proxy_scheme = "http"; + const char *proxy_host = cf->conn->http_proxy.peer->hostname; + int httpversion = proxy_http_ver_major(ver); + char *authority = NULL; + char *path = NULL; + char *encoded_host = NULL; + struct httpreq *req = NULL; + bool proxy_ipv6_ip; + CURLcode result; + + if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS2 || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS3) + proxy_scheme = "https"; + + proxy_ipv6_ip = cf->conn->http_proxy.peer->ipv6 != 0; + + authority = curl_maprintf("%s%s%s:%d", + proxy_ipv6_ip ? "[" : "", + proxy_host, + proxy_ipv6_ip ? "]" : "", + cf->conn->http_proxy.peer->port); + if(!authority) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(dest->ipv6) { + /* RFC 9298: colons in IPv6 addresses MUST be percent-encoded + * in the URI template (e.g. "2001:db8::1" -> "2001%3Adb8%3A%3A1") */ + const char *s = dest->hostname; + char *d; + size_t hlen = strlen(s); + encoded_host = curlx_malloc(hlen * 3 + 1); + if(!encoded_host) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + d = encoded_host; + while(*s) { + if(*s == ':') { + *d++ = '%'; + *d++ = '3'; + *d++ = 'A'; + } + else + *d++ = *s; + s++; + } + *d = '\0'; + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + encoded_host, (unsigned int)dest->port); + } + else { + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + dest->hostname, (unsigned int)dest->port); + } + + if(!path) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_http_req_make(&req, "GET", sizeof("GET")-1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else if(ver == PROXY_HTTP_V2 || ver == PROXY_HTTP_V3) { + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else { + result = CURLE_FAILED_INIT; + goto out; + } + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, + req->authority, NULL, TRUE); + if(result) + goto out; + + /* If user is not overriding Host: header, we add for HTTP/1.x */ + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + result = Curl_dynhds_cadd(&req->headers, "Host", authority); + if(result) + goto out; + } + + if(data->req.hd_proxy_auth) { + result = Curl_dynhds_h1_cadd_line(&req->headers, + data->req.hd_proxy_auth); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", + data->set.str[STRING_USERAGENT]); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_dynhds_cadd(&req->headers, "Connection", "Upgrade"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Upgrade", "connect-udp"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + else { + result = Curl_dynhds_cadd(&req->headers, ":Protocol", "connect-udp"); + if(result) + goto out; + + if(ver >= PROXY_HTTP_V2) { + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + } + + result = dynhds_add_custom(data, TRUE, httpversion, + TRUE, &req->headers); + +out: + if(result && req) { + Curl_http_req_free(req); + req = NULL; + } + curlx_free(authority); + curlx_free(path); + curlx_free(encoded_host); + *preq = req; + return result; +} + +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel) +{ + CURLcode result; + + if(udp_tunnel) + result = http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver); + else + result = http_proxy_create_CONNECT(preq, cf, data, dest, ver); + if(result) + return result; + + if(udp_tunnel) + infof(data, "Establishing %s proxy UDP tunnel to %s:%s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + data->state.up.hostname, data->state.up.port); + else + infof(data, "Establishing %s proxy tunnel to %s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + (*preq)->authority); + return CURLE_OK; +} + +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult) +{ + struct dynhds_entry *capsule_protocol = NULL; + struct dynhds_entry *auth_reply = NULL; + size_t i, header_count; + CURLcode result = CURLE_OK; + + DEBUGASSERT(resp); + + header_count = Curl_dynhds_count(&resp->headers); + if(udp_tunnel) + infof(data, "CONNECT-UDP Response Status %d", resp->status); + else + infof(data, "CONNECT Response Status %d", resp->status); + infof(data, "Response Headers (%zu total):", header_count); + for(i = 0; i < header_count; i++) { + struct dynhds_entry *entry = Curl_dynhds_getn(&resp->headers, i); + if(entry) + infof(data, " %s: %s", entry->name, entry->value); + } + + if(resp->status == 401) { + auth_reply = Curl_dynhds_cget(&resp->headers, "WWW-Authenticate"); + } + else if(resp->status == 407) { + auth_reply = Curl_dynhds_cget(&resp->headers, "Proxy-Authenticate"); + } + + if(auth_reply) { + CURL_TRC_CF(data, cf, "[0] CONNECT%s: fwd auth header '%s'", + udp_tunnel ? "-UDP" : "", auth_reply->value); + result = Curl_http_input_auth(data, resp->status == 407, + auth_reply->value); + if(result) + return result; + if(data->req.newurl) { + curlx_safefree(data->req.newurl); + *presult = PROXY_INSPECT_AUTH_RETRY; + return CURLE_OK; + } + } + + if(udp_tunnel) { + if(resp->status / 100 == 2) { + capsule_protocol = Curl_dynhds_cget(&resp->headers, + "capsule-protocol"); + if(capsule_protocol) { + if(!strncmp(capsule_protocol->value, "?1", 2) && + !capsule_protocol->value[2]) { + infof(data, "CONNECT-UDP tunnel established, response %d", + resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + failf(data, "Failed to establish CONNECT-UDP tunnel, response %d, " + "unsupported capsule-protocol value '%s'", + resp->status, capsule_protocol->value); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + else { + /* NOTE proxies may not set capsule protocol in the headers */ + infof(data, "CONNECT-UDP tunnel established, response %d " + "but no capsule-protocol header found", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + } + else { + failf(data, "Failed to establish CONNECT-UDP tunnel, " + "response %d", resp->status); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + } + + if(resp->status / 100 == 2) { + infof(data, "CONNECT tunnel established, response %d", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; +} + static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; + bool udp_tunnel = TRNSPRT_IS_DGRAM(ctx->tunnel_transport); + const char *tunnel_type = udp_tunnel ? "CONNECT-UDP" : "CONNECT"; if(cf->connected) { *done = TRUE; return CURLE_OK; } - CURL_TRC_CF(data, cf, "connect"); + CURL_TRC_CF(data, cf, "%s", tunnel_type); connect_sub: - result = cf->next->cft->do_connect(cf->next, data, done); - if(result || !*done) - return result; + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } *done = FALSE; if(!ctx->sub_filter_installed) { - int httpversion = 0; - const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + const char *alpn = NULL; + + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + } if(alpn) - infof(data, "CONNECT: '%s' negotiated", alpn); - else - infof(data, "CONNECT: no ALPN negotiated"); + infof(data, "%s: '%s' negotiated", tunnel_type, alpn); + else if(!alpn) { + /* No ALPN, proxytype rules. Fake ALPN */ + infof(data, "%s: no ALPN negotiated", tunnel_type); + switch(ctx->proxytype) { + case CURLPROXY_HTTP_1_0: + alpn = "http/1.0"; + break; + case CURLPROXY_HTTPS2: + alpn = "h2"; + break; + case CURLPROXY_HTTPS3: + alpn = "h3"; + break; + default: + alpn = "http/1.1"; + break; + } + } - if(alpn && !strcmp(alpn, "http/1.0")) { + if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, 10, + udp_tunnel); if(result) goto out; - httpversion = 10; } - else if(!alpn || !strcmp(alpn, "http/1.1")) { - CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + else if(!strcmp(alpn, "http/1.1")) { + int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", + httpversion % 10); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, + httpversion, udp_tunnel); if(result) goto out; - /* Assume that without an ALPN, we are talking to an ancient one */ - httpversion = 11; } #ifdef USE_NGHTTP2 else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); - result = Curl_cf_h2_proxy_insert_after(cf, data); + result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->tunnel_peer, + udp_tunnel); + if(result) + goto out; + } +#endif /* USE_NGHTTP2 */ +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + else if(!strcmp(alpn, "h3")) { + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3"); + result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->peer, ctx->peer, + ctx->tunnel_peer, + ctx->tunnel_transport); if(result) goto out; - httpversion = 20; } -#endif +#endif /* USE_PROXY_HTTP3 && USE_NGHTTP3 && USE_NGTCP2 && USE_OPENSSL */ else { - failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn); + failf(data, "%s: negotiated ALPN '%s' not supported", tunnel_type, alpn); result = CURLE_COULDNT_CONNECT; goto out; } ctx->sub_filter_installed = TRUE; - ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ @@ -334,8 +653,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, } else { /* subchain connected and we had already installed the protocol filter. - * This means the protocol tunnel is established, we are done. - */ + * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->sub_filter_installed); result = CURLE_OK; } @@ -348,14 +666,15 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { + struct cf_proxy_ctx *ctx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->tunnel_peer->port; + *((const char **)pres2) = ctx->tunnel_peer->hostname; return CURLE_OK; case CF_QUERY_ALPN_NEGOTIATED: { const char **palpn = pres2; @@ -371,22 +690,23 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } -static void http_proxy_cf_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx) { - struct cf_proxy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); + if(ctx) { + Curl_peer_unlink(&ctx->peer); + Curl_peer_unlink(&ctx->tunnel_peer); + curlx_free(ctx); + } } -static void http_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void http_proxy_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) { - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); + struct cf_proxy_ctx *ctx = cf->ctx; + if(ctx) { + CURL_TRC_CF(data, cf, "destroy"); + cf_https_proxy_ctx_free(ctx); + } } struct Curl_cftype Curl_cft_http_proxy = { @@ -395,7 +715,6 @@ struct Curl_cftype Curl_cft_http_proxy = { 0, http_proxy_cf_destroy, http_proxy_cf_connect, - http_proxy_cf_close, Curl_cf_def_shutdown, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, @@ -404,22 +723,34 @@ struct Curl_cftype Curl_cft_http_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_http_proxy_query, }; CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, + uint8_t proxytype) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; CURLcode result; (void)data; + if(!peer || !tunnel_peer) + return CURLE_FAILED_INIT; + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + Curl_peer_link(&ctx->peer, peer); + Curl_peer_link(&ctx->tunnel_peer, tunnel_peer); + ctx->proxytype = proxytype; + ctx->tunnel_transport = tunnel_transport; + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) goto out; @@ -427,8 +758,18 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_conn_cf_insert_after(cf_at, cf); out: - curlx_free(ctx); + cf_https_proxy_ctx_free(ctx); return result; } +uint8_t Curl_http_proxy_transport(uint8_t proxytype) +{ + switch(proxytype) { + case CURLPROXY_HTTPS3: + return TRNSPRT_QUIC; + default: + return TRNSPRT_TCP; + } +} + #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ diff --git a/deps/curl/lib/http_proxy.h b/deps/curl/lib/http_proxy.h index 155b222edc..86c9088093 100644 --- a/deps/curl/lib/http_proxy.h +++ b/deps/curl/lib/http_proxy.h @@ -32,33 +32,51 @@ enum Curl_proxy_use { HEADER_SERVER, /* direct to server */ HEADER_PROXY, /* regular request to proxy */ - HEADER_CONNECT /* sending CONNECT to a proxy */ + HEADER_CONNECT, /* sending CONNECT to a proxy */ + HEADER_CONNECT_UDP /* sending CONNECT-UDP to a proxy */ }; -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip); +/* HTTP version for proxy tunnel request creation */ +typedef enum { + PROXY_HTTP_V1 = 1, + PROXY_HTTP_V2 = 2, + PROXY_HTTP_V3 = 3 +} proxy_http_ver; -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - int http_version_major); +/* Result from inspecting a proxy tunnel response */ +typedef enum { + PROXY_INSPECT_OK, /* Tunnel established */ + PROXY_INSPECT_FAILED, /* Tunnel failed */ + PROXY_INSPECT_AUTH_RETRY /* Retry with auth */ +} proxy_inspect_result; + +/* Create CONNECT or CONNECT-UDP request */ +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel); + +/* Inspect tunnel response for H2/H3 proxy (capsule-protocol, auth) */ +struct http_resp; +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600 * 1000) -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2); - CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, + uint8_t proxytype); extern struct Curl_cftype Curl_cft_http_proxy; -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +uint8_t Curl_http_proxy_transport(uint8_t proxytype); -#define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ - ((t) == CURLPROXY_HTTPS2)) +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/deps/curl/lib/httpsrr.c b/deps/curl/lib/httpsrr.c index 90fcb524e8..53647b81ed 100644 --- a/deps/curl/lib/httpsrr.c +++ b/deps/curl/lib/httpsrr.c @@ -251,7 +251,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, return FALSE; return (!rr->target || !rr->target[0] || (rr->target[0] == '.' && !rr->target[1])) && - (!rr->port_set || rr->port == data->conn->remote_port); + (!rr->port_set || rr->port == data->conn->origin->port); } #ifdef USE_ARES diff --git a/deps/curl/lib/httpsrr.h b/deps/curl/lib/httpsrr.h index 28a790d17f..2ee1beab3e 100644 --- a/deps/curl/lib/httpsrr.h +++ b/deps/curl/lib/httpsrr.h @@ -31,7 +31,7 @@ #ifdef USE_HTTPSRR -#define CURL_MAXLEN_host_name 253 +#define CURL_MAXLEN_HOST_NAME 253 #define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; diff --git a/deps/curl/lib/idn.c b/deps/curl/lib/idn.c index f2b954e2d0..b26f251d97 100644 --- a/deps/curl/lib/idn.c +++ b/deps/curl/lib/idn.c @@ -27,6 +27,7 @@ #include "curl_setup.h" #include "urldata.h" +#include "curlx/strparse.h" #include "idn.h" #ifdef USE_LIBIDN2 @@ -222,15 +223,24 @@ static CURLcode win32_ascii_to_idn(const char *in, char **out) */ bool Curl_is_ASCII_name(const char *hostname) { - /* get an UNSIGNED local version of the pointer */ - const unsigned char *ch = (const unsigned char *)hostname; - - if(!hostname) /* bad input, consider it ASCII! */ - return TRUE; + if(hostname) { + struct Curl_str s; + s.str = hostname; + s.len = strlen(hostname); + return Curl_is_ASCII_str(&s); + } + return TRUE; +} - while(*ch) { - if(*ch++ & 0x80) - return FALSE; +bool Curl_is_ASCII_str(struct Curl_str *s) +{ + if(s && s->len) { + const unsigned char *ch = (const unsigned char *)s->str; + size_t i; + for(i = 0; i < s->len; ++i) { + if(ch[i] & 0x80) + return FALSE; + } } return TRUE; } diff --git a/deps/curl/lib/idn.h b/deps/curl/lib/idn.h index 90d8e811b1..c73b870a70 100644 --- a/deps/curl/lib/idn.h +++ b/deps/curl/lib/idn.h @@ -23,7 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + +struct Curl_str; +struct hostname; + bool Curl_is_ASCII_name(const char *hostname); +bool Curl_is_ASCII_str(struct Curl_str *s); + CURLcode Curl_idnconvert_hostname(struct hostname *host); #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) diff --git a/deps/curl/lib/if2ip.c b/deps/curl/lib/if2ip.c index b71254ada0..fd34f204b5 100644 --- a/deps/curl/lib/if2ip.c +++ b/deps/curl/lib/if2ip.c @@ -107,7 +107,7 @@ if2ip_result_t Curl_if2ip(int af, #endif if(getifaddrs(&head) >= 0) { - for(iface = head; iface != NULL; iface = iface->ifa_next) { + for(iface = head; iface; iface = iface->ifa_next) { if(iface->ifa_addr) { if(iface->ifa_addr->sa_family == af) { if(curl_strequal(iface->ifa_name, interf)) { diff --git a/deps/curl/lib/if2ip.h b/deps/curl/lib/if2ip.h index 12fdaabd73..dc79c383b7 100644 --- a/deps/curl/lib/if2ip.h +++ b/deps/curl/lib/if2ip.h @@ -54,7 +54,7 @@ if2ip_result_t Curl_if2ip(int af, #ifdef __INTERIX -/* Nedelcho Stanev's work-around for SFU 3.0 */ +/* Nedelcho Stanev's workaround for SFU 3.0 */ struct ifreq { #define IFNAMSIZ 16 #define IFHWADDRLEN 6 diff --git a/deps/curl/lib/imap.c b/deps/curl/lib/imap.c index 5ef2a2cb21..976c9f573e 100644 --- a/deps/curl/lib/imap.c +++ b/deps/curl/lib/imap.c @@ -555,7 +555,8 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, bool ssldone = FALSE; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ @@ -565,7 +566,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!imapc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { imapc->ssldone = ssldone; /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ @@ -597,15 +598,15 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!conn->creds) { imap_state(data, imapc, IMAP_STOP); return result; } /* Make sure the username and password are in the correct atom format */ - user = imap_atom(conn->user, FALSE); - passwd = imap_atom(conn->passwd, FALSE); + user = imap_atom(Curl_creds_user(conn->creds), FALSE); + passwd = imap_atom(Curl_creds_passwd(conn->creds), FALSE); /* Send the LOGIN command */ result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "", @@ -712,7 +713,6 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, /* Calculate the SASL login details */ result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, &progress); - if(!result) { if(progress == SASL_INPROGRESS) imap_state(data, imapc, IMAP_AUTHENTICATE); @@ -911,12 +911,12 @@ static CURLcode imap_perform_append(struct Curl_easy *data, if(data->set.upload_flags) { int i; struct ulbits ulflag[] = { - {CURLULFLAG_ANSWERED, "Answered"}, - {CURLULFLAG_DELETED, "Deleted"}, - {CURLULFLAG_DRAFT, "Draft"}, - {CURLULFLAG_FLAGGED, "Flagged"}, - {CURLULFLAG_SEEN, "Seen"}, - {0, NULL} + { CURLULFLAG_ANSWERED, "Answered" }, + { CURLULFLAG_DELETED, "Deleted" }, + { CURLULFLAG_DRAFT, "Draft" }, + { CURLULFLAG_FLAGGED, "Flagged" }, + { CURLULFLAG_SEEN, "Seen" }, + { 0, NULL } }; result = CURLE_OUT_OF_MEMORY; @@ -1044,7 +1044,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, /* Extract the word */ for(wordlen = 0; line[wordlen] && !ISBLANK(line[wordlen]) && - !ISNEWLINE(line[wordlen]);) + !ISNEWLINE(line[wordlen]);) wordlen++; /* Does the server support the STARTTLS capability? */ @@ -1076,7 +1076,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { + else if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1189,9 +1189,9 @@ static bool is_custom_fetch_listing_match(const char *params) return FALSE; } if(*params == ':') - return true; + return TRUE; if(*params == ',') - return true; + return TRUE; return FALSE; } @@ -1796,7 +1796,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn, static CURLcode imap_parse_url_path(struct Curl_easy *data, struct IMAP *imap) { - /* The imap struct is already initialised in imap_connect() */ + /* The imap struct is already initialized in imap_connect() */ CURLcode result = CURLE_OK; const char *begin = &data->state.up.path[1]; /* skip leading slash */ const char *ptr = begin; diff --git a/deps/curl/lib/ldap.c b/deps/curl/lib/ldap.c index 16c93eeca2..a6a69eb7f1 100644 --- a/deps/curl/lib/ldap.c +++ b/deps/curl/lib/ldap.c @@ -45,7 +45,7 @@ #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ # include -/* Undefine indirect symbols conflicting with BoringSSL/AWS-LC. */ +/* Undefine indirect symbols conflicting with AWS-LC/BoringSSL. */ # undef X509_NAME # undef X509_EXTENSIONS # undef PKCS7_ISSUER_AND_SERIAL @@ -230,6 +230,27 @@ static ULONG ldap_win_bind(struct Curl_easy *data, LDAP *server, } #endif /* USE_WIN32_LDAP */ +static bool ldap_value_needs_base64(const char *attr, size_t attr_len, + const BerValue *val) +{ + ber_len_t j; + + if((attr_len > 7) && curl_strequal(";binary", attr + attr_len - 7)) + return TRUE; + + /* check for leading or trailing whitespace */ + if(val->bv_len && (ISBLANK(val->bv_val[0]) || + ISBLANK(val->bv_val[val->bv_len - 1]))) + return TRUE; + + /* check for unprintable characters */ + for(j = 0; j < val->bv_len; j++) + if(!ISPRINT(val->bv_val[j])) + return TRUE; + + return FALSE; +} + static CURLcode ldap_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -242,20 +263,21 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; int ldap_proto = LDAP_VERSION3; int ldap_ssl = 0; - char *val_b64 = NULL; - size_t val_b64_sz = 0; #ifdef LDAP_OPT_NETWORK_TIMEOUT - struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ + struct timeval ldap_timeout = { 10, 0 }; /* 10s connection/search timeout */ #endif #ifdef USE_WIN32_LDAP TCHAR *host = NULL; #else char *host = NULL; #endif - char *user = NULL; - char *passwd = NULL; + const char *user = Curl_creds_has_user(data->state.creds) ? + data->state.creds->user : NULL; + const char *passwd = Curl_creds_has_passwd(data->state.creds) ? + data->state.creds->passwd : NULL; struct ip_quadruple ipquad; bool is_ipv6; + BerElement *ber = NULL; *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", @@ -284,21 +306,16 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_ssl ? "encrypted" : "cleartext"); #ifdef USE_WIN32_LDAP - host = curlx_convert_UTF8_to_tchar(conn->host.name); + host = curlx_convert_UTF8_to_tchar(conn->origin->hostname); if(!host) { result = CURLE_OUT_OF_MEMORY; goto quit; } #else - host = conn->host.name; + host = conn->origin->hostname; #endif - if(data->state.aptr.user) { - user = conn->user; - passwd = conn->passwd; - } - #ifdef USE_WIN32_LDAP if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); @@ -307,7 +324,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); if(!server) { failf(data, "LDAP: cannot setup connect to %s:%u", - conn->host.dispname, ipquad.remote_port); + conn->origin->user_hostname, ipquad.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -317,6 +334,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #endif ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + /* Do not chase referrals. */ + ldap_set_option(server, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP @@ -328,8 +348,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef LDAP_OPT_X_TLS if(conn->ssl_config.verifypeer) { /* OpenLDAP SDK supports BASE64 files. */ - if(data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "PEM")) { + if(data->set.ssl.primary.cert_type && + !curl_strequal(data->set.ssl.primary.cert_type, "PEM")) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; @@ -427,7 +447,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) for(entryIterator = ldap_first_entry(server, ldapmsg); entryIterator; entryIterator = ldap_next_entry(server, entryIterator), num++) { - BerElement *ber = NULL; #ifdef USE_WIN32_LDAP TCHAR *attribute; #else @@ -437,23 +456,17 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* Get the DN and write it to the client */ { - char *name; + char *name = NULL; size_t name_len = 0; #ifdef USE_WIN32_LDAP TCHAR *dn = ldap_get_dn(server, entryIterator); - name = curlx_convert_tchar_to_UTF8(dn); - if(!name) { - ldap_memfree(dn); - - result = CURLE_OUT_OF_MEMORY; - - goto quit; - } + if(dn) + name = curlx_convert_tchar_to_UTF8(dn); #else char *dn = name = ldap_get_dn(server, entryIterator); #endif if(!name) - result = CURLE_FAILED_INIT; + result = dn ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; else { name_len = strlen(name); result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4); @@ -477,11 +490,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef USE_WIN32_LDAP char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { - if(ber) - ber_free(ber, 0); - + ldap_memfree(attribute); result = CURLE_OUT_OF_MEMORY; - goto quit; } #else @@ -491,15 +501,12 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) vals = ldap_get_values_len(server, entryIterator, attribute); if(vals) { - for(i = 0; (vals[i] != NULL); i++) { + for(i = 0; vals[i]; i++) { result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -508,25 +515,21 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } - result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); + result = Curl_client_write(data, CLIENTWRITE_BODY, ":", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } - if((attr_len > 7) && - curl_strequal(";binary", attr + (attr_len - 7))) { + if(ldap_value_needs_base64(attr, attr_len, vals[i])) { + char *val_b64 = NULL; + size_t val_b64_sz = 0; + /* Binary attribute, encode to base64. */ if(vals[i]->bv_len) { result = curlx_base64_encode((uint8_t *)vals[i]->bv_val, @@ -536,38 +539,48 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } + } - if(val_b64_sz > 0) { - result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, - val_b64_sz); + result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); + if(result) { + curlx_free(val_b64); + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + goto quit; + } + + if(val_b64_sz) { + result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + if(result) { curlx_free(val_b64); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + goto quit; } } + + curlx_free(val_b64); } else { - result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val, - vals[i]->bv_len); + result = Curl_client_write(data, CLIENTWRITE_BODY, " ", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); + goto quit; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + vals[i]->bv_val, vals[i]->bv_len); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); goto quit; } } @@ -577,9 +590,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } } @@ -597,11 +607,15 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - if(ber) + if(ber) { ber_free(ber, 0); + ber = NULL; + } } quit: + if(ber) + ber_free(ber, 0); if(ldapmsg) { ldap_msgfree(ldapmsg); LDAP_TRACE(("Received %d entries\n", num)); @@ -681,8 +695,8 @@ static size_t num_entries(const char *s) * Syntax: * ldap://:/???? * - * already known from 'conn->host.name'. - * already known from 'conn->remote_port'. + * already known from 'conn->origin->hostname'. + * already known from 'conn->origin->port'. * extract the rest from 'data->state.path+1'. All fields are optional. * e.g. * ldap://:/??? @@ -708,8 +722,8 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; - ludp->lud_port = conn->remote_port; - ludp->lud_host = conn->host.name; + ludp->lud_port = conn->origin->port; + ludp->lud_host = conn->origin->hostname; /* Duplicate the path */ p = path = curlx_strdup(data->state.up.path + 1); diff --git a/deps/curl/lib/llist.c b/deps/curl/lib/llist.c index 3ec85e4a2c..ce0c59cc50 100644 --- a/deps/curl/lib/llist.c +++ b/deps/curl/lib/llist.c @@ -200,7 +200,7 @@ void Curl_llist_destroy(struct Curl_llist *list, void *user) /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ -struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) +struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -213,8 +213,8 @@ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list); -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list); +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -223,7 +223,7 @@ UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) #endif /* Curl_llist_count() returns a size_t the number of nodes in the list */ -size_t Curl_llist_count(struct Curl_llist *list) +size_t Curl_llist_count(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -231,7 +231,7 @@ size_t Curl_llist_count(struct Curl_llist *list) } /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n) +void *Curl_node_elem(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -240,7 +240,7 @@ void *Curl_node_elem(struct Curl_llist_node *n) /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ -struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) +struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -253,8 +253,10 @@ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n); -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -262,7 +264,7 @@ UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) } #endif -struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(!n->_list || n->_init == NODEINIT); diff --git a/deps/curl/lib/llist.h b/deps/curl/lib/llist.h index 28e958d5c5..de4adc972f 100644 --- a/deps/curl/lib/llist.h +++ b/deps/curl/lib/llist.h @@ -60,13 +60,13 @@ void Curl_llist_destroy(struct Curl_llist *list, void *user); /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which might be NULL */ -struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); +struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list); /* Curl_llist_count() returns a size_t the number of nodes in the list */ -size_t Curl_llist_count(struct Curl_llist *list); +size_t Curl_llist_count(const struct Curl_llist *list); /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n); +void *Curl_node_elem(const struct Curl_llist_node *n); /* Remove the node from the list and return the custom data * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ @@ -74,9 +74,9 @@ void *Curl_node_take_elem(struct Curl_llist_node *e); /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ -struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); +struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n); /* Curl_node_llist() return the list the node is in or NULL. */ -struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n); +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n); #endif /* HEADER_CURL_LLIST_H */ diff --git a/deps/curl/lib/md4.c b/deps/curl/lib/md4.c index 0213483ad3..e030ffac30 100644 --- a/deps/curl/lib/md4.c +++ b/deps/curl/lib/md4.c @@ -158,6 +158,7 @@ static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) #elif defined(USE_GNUTLS) #include +#include typedef struct md4_ctx my_md4_ctx; @@ -175,7 +176,11 @@ static void my_md4_update(my_md4_ctx *ctx, static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md4_digest(ctx, digest); +#else md4_digest(ctx, MD4_DIGEST_SIZE, digest); +#endif } #else diff --git a/deps/curl/lib/md5.c b/deps/curl/lib/md5.c index 4dd0d7c278..1f1b4f8ad6 100644 --- a/deps/curl/lib/md5.c +++ b/deps/curl/lib/md5.c @@ -47,6 +47,7 @@ #ifdef USE_GNUTLS #include +#include typedef struct md5_ctx my_md5_ctx; @@ -64,7 +65,11 @@ static void my_md5_update(void *ctx, static void my_md5_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md5_digest(ctx, digest); +#else md5_digest(ctx, 16, digest); +#endif } #elif defined(USE_OPENSSL) && \ @@ -116,7 +121,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 #include typedef psa_hash_operation_t my_md5_ctx; diff --git a/deps/curl/lib/memdebug.c b/deps/curl/lib/memdebug.c index 6eda7a8236..b6af2c6d35 100644 --- a/deps/curl/lib/memdebug.c +++ b/deps/curl/lib/memdebug.c @@ -269,7 +269,7 @@ char *curl_dbg_strdup(const char *str, int line, const char *source) char *mem; size_t len; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("strdup", line, source)) return NULL; @@ -294,7 +294,7 @@ wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) wchar_t *mem; size_t wsiz, bsiz; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("wcsdup", line, source)) return NULL; @@ -510,7 +510,7 @@ int curl_dbg_fclose(FILE *file, int line, const char *source) { int res; - DEBUGASSERT(file != NULL); + DEBUGASSERT(file); if(source) curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); diff --git a/deps/curl/lib/mime.c b/deps/curl/lib/mime.c index b7e51aae4e..077e599e43 100644 --- a/deps/curl/lib/mime.c +++ b/deps/curl/lib/mime.c @@ -1123,7 +1123,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, curl_mime *mime; curl_mimepart *d; const curl_mimepart *s; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; DEBUGASSERT(dst); @@ -1132,66 +1132,67 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, case MIMEKIND_NONE: break; case MIMEKIND_DATA: - res = curl_mime_data(dst, src->data, (size_t)src->datasize); + result = curl_mime_data(dst, src->data, (size_t)src->datasize); break; case MIMEKIND_FILE: - res = curl_mime_filedata(dst, src->data); + result = curl_mime_filedata(dst, src->data); /* Do not abort duplication if file is not readable. */ - if(res == CURLE_READ_ERROR) - res = CURLE_OK; + if(result == CURLE_READ_ERROR) + result = CURLE_OK; break; case MIMEKIND_CALLBACK: - res = curl_mime_data_cb(dst, src->datasize, src->readfunc, - src->seekfunc, src->freefunc, src->arg); + result = curl_mime_data_cb(dst, src->datasize, src->readfunc, + src->seekfunc, src->freefunc, src->arg); break; case MIMEKIND_MULTIPART: /* No one knows about the cloned subparts, thus always attach ownership to the part. */ mime = curl_mime_init(data); - res = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; + result = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; /* Duplicate subparts. */ - for(s = ((curl_mime *)src->arg)->firstpart; !res && s; s = s->nextpart) { + for(s = ((curl_mime *)src->arg)->firstpart; !result && s; + s = s->nextpart) { d = curl_mime_addpart(mime); - res = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; + result = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; } break; default: /* Invalid kind: should not occur. */ DEBUGF(infof(data, "invalid MIMEKIND* attempt")); - res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ + result = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ break; } /* Duplicate headers. */ - if(!res && src->userheaders) { + if(!result && src->userheaders) { struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); if(!hdrs) - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { /* No one but this procedure knows about the new header list, so always take ownership. */ - res = curl_mime_headers(dst, hdrs, TRUE); - if(res) + result = curl_mime_headers(dst, hdrs, TRUE); + if(result) curl_slist_free_all(hdrs); } } - if(!res) { + if(!result) { /* Duplicate other fields. */ dst->encoder = src->encoder; - res = curl_mime_type(dst, src->mimetype); + result = curl_mime_type(dst, src->mimetype); } - if(!res) - res = curl_mime_name(dst, src->name); - if(!res) - res = curl_mime_filename(dst, src->filename); + if(!result) + result = curl_mime_name(dst, src->name); + if(!result) + result = curl_mime_filename(dst, src->filename); /* If an error occurred, rollback. */ - if(res) + if(result) Curl_mime_cleanpart(dst); - return res; + return result; } /* @@ -1201,9 +1202,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, /* Create a mime handle. */ curl_mime *curl_mime_init(void *easy) { - curl_mime *mime; - - mime = (curl_mime *)curlx_malloc(sizeof(*mime)); + curl_mime *mime = curlx_malloc(sizeof(*mime)); if(mime) { mime->parent = NULL; @@ -1240,7 +1239,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime) if(!mime) return NULL; - part = (curl_mimepart *)curlx_malloc(sizeof(*part)); + part = curlx_malloc(sizeof(*part)); if(part) { Curl_mime_initpart(part); @@ -1692,6 +1691,52 @@ static bool content_type_match(const char *contenttype, return FALSE; } +static CURLcode add_content_disposition(struct Curl_easy *data, + curl_mimepart *part, + const char *disposition, + const char *contenttype, + enum mimestrategy strategy) +{ + if(!disposition) + if(part->filename || part->name || + (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) + disposition = DISPOSITION_DEFAULT; + if(disposition && curl_strequal(disposition, "attachment") && + !part->name && !part->filename) + disposition = NULL; + if(disposition) { + CURLcode result = CURLE_OK; + char *name = NULL; + char *filename = NULL; + + if(part->name) { + name = escape_string(data, part->name, strategy); + if(!name) + return CURLE_OUT_OF_MEMORY; + } + if(part->filename) { + filename = escape_string(data, part->filename, strategy); + if(!filename) + result = CURLE_OUT_OF_MEMORY; + } + if(!result) + result = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name ? "; name=\"" : "", + name ? name : "", + name ? "\"" : "", + filename ? "; filename=\"" : "", + filename ? filename : "", + filename ? "\"" : ""); + curlx_safefree(name); + curlx_safefree(filename); + if(result) + return result; + } + return CURLE_OK; +} + CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, curl_mimepart *part, const char *contenttype, @@ -1750,42 +1795,10 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, /* Issue content-disposition header only if not already set by caller. */ if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { - if(!disposition) - if(part->filename || part->name || - (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) - disposition = DISPOSITION_DEFAULT; - if(disposition && curl_strequal(disposition, "attachment") && - !part->name && !part->filename) - disposition = NULL; - if(disposition) { - char *name = NULL; - char *filename = NULL; - - if(part->name) { - name = escape_string(data, part->name, strategy); - if(!name) - result = CURLE_OUT_OF_MEMORY; - } - if(!result && part->filename) { - filename = escape_string(data, part->filename, strategy); - if(!filename) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) - result = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name ? "; name=\"" : "", - name ? name : "", - name ? "\"" : "", - filename ? "; filename=\"" : "", - filename ? filename : "", - filename ? "\"" : ""); - curlx_safefree(name); - curlx_safefree(filename); - if(result) - return result; - } + result = add_content_disposition(data, part, disposition, + contenttype, strategy); + if(result) + return result; } /* Issue Content-Type header. */ @@ -1896,7 +1909,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, /* Once we have errored, we will return the same error forever */ if(ctx->errored) { CURL_TRC_READ(data, "cr_mime_read(len=%zu) is errored -> %d, eos=0", - blen, ctx->error_result); + blen, (int)ctx->error_result); *pnread = 0; *peos = FALSE; return ctx->error_result; @@ -1927,7 +1940,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, else if(blen <= 4) { /* Curl_mime_read() may go into an infinite loop when reading * via a base64 encoder, as it stalls when the read buffer is too small - * to contain a complete 3 byte encoding. Read into a larger buffer + * to contain a complete 3-byte encoding. Read into a larger buffer * and use that until empty. */ CURL_TRC_READ(data, "cr_mime_read(len=%zu), small read, using tmp", blen); nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part); @@ -2013,8 +2026,8 @@ static CURLcode cr_mime_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%" FMT_OFF_T ") -> %d, %zu, %d", - blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, + ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } diff --git a/deps/curl/lib/mprintf.c b/deps/curl/lib/mprintf.c index 6eaea66b70..230b554191 100644 --- a/deps/curl/lib/mprintf.c +++ b/deps/curl/lib/mprintf.c @@ -244,7 +244,7 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, fmt += 2; } else { -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; @@ -267,14 +267,14 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, case 'z': /* the code below generates a warning if -Wunreachable-code is used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) +#if SIZEOF_SIZE_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; #endif break; case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; @@ -423,7 +423,6 @@ static bool parse_conversion(const char f, unsigned int *flagp, return FALSE; } - static int parsefmt(const char *format, struct outsegment *out, struct va_input *in, @@ -968,7 +967,6 @@ static bool out_pointer(void *userp, * * All output is sent to the 'stream()' callback, one byte at a time. */ - static int formatf(void *userp, /* untouched by format(), sent to the stream() function in the second argument */ /* function pointer called for each output character */ @@ -1075,9 +1073,8 @@ static int formatf(void *userp, /* untouched by format(), sent to the /* Answer the count of characters written. */ if(p.flags & FLAGS_LONGLONG) *(int64_t *)iptr->val.ptr = (int64_t)done; - else - if(p.flags & FLAGS_LONG) - *(long *)iptr->val.ptr = (long)done; + else if(p.flags & FLAGS_LONG) + *(long *)iptr->val.ptr = (long)done; else if(!(p.flags & FLAGS_SHORT)) *(int *)iptr->val.ptr = done; else diff --git a/deps/curl/lib/mqtt.c b/deps/curl/lib/mqtt.c index 84fd272e21..8482477b97 100644 --- a/deps/curl/lib/mqtt.c +++ b/deps/curl/lib/mqtt.c @@ -280,11 +280,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data) char *packet = NULL; /* extracting username from request */ - const char *username = data->state.aptr.user ? data->state.aptr.user : ""; - const size_t ulen = strlen(username); - /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : ""; - const size_t plen = strlen(passwd); + struct Curl_creds *creds = data->state.creds; + const size_t ulen = creds ? strlen(creds->user) : 0; + const size_t plen = creds ? strlen(creds->passwd) : 0; const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 + /* The plus 2s below are for the MSB and LSB describing the length of the string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ @@ -326,7 +324,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) if(ulen) { start_pwd += 2; - rc = add_user(username, ulen, + rc = add_user(creds->user, ulen, (unsigned char *)packet, start_user, remain_pos); if(rc) { failf(data, "Username too long: [%zu]", ulen); @@ -337,7 +335,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) /* if passwd was provided, add it to the packet */ if(plen) { - rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); + rc = add_passwd(creds->passwd, plen, packet, start_pwd, remain_pos); if(rc) { failf(data, "Password too long: [%zu]", plen); result = CURLE_WEIRD_SERVER_REPLY; @@ -351,8 +349,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) end: if(packet) curlx_free(packet); - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); + Curl_creds_unlink(&data->state.creds); return result; } @@ -427,7 +424,7 @@ static CURLcode mqtt_verify_connack(struct Curl_easy *data) if(ptr[0] != 0x00 || ptr[1] != 0x00) { failf(data, "Expected %02x%02x but got %02x%02x", - 0x00, 0x00, ptr[0], ptr[1]); + 0x00U, 0x00U, (unsigned char)ptr[0], (unsigned char)ptr[1]); curlx_dyn_reset(&mq->recvbuf); return CURLE_WEIRD_SERVER_REPLY; } @@ -605,9 +602,9 @@ static CURLcode mqtt_publish(struct Curl_easy *data) return result; } -/* return 0 on success, non-zero on error */ -static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, - size_t buflen) +/* return FALSE on success, TRUE on error */ +static bool mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -616,14 +613,17 @@ static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, for(i = 0; (i < buflen) && (encoded & 128); i++) { if(i == 4) - return 1; /* bad size */ + return TRUE; /* bad size */ encoded = buf[i]; len += (encoded & 127) * mult; mult *= 128; } + if(encoded & 128) + /* truncated size */ + return TRUE; *lenp = len; - return 0; + return FALSE; } #if defined(DEBUGBUILD) && defined(CURLVERBOSE) @@ -774,7 +774,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done) result = mqtt_connect(data); if(result) { - failf(data, "Error %d sending MQTT CONNECT request", result); + failf(data, "Error %d sending MQTT CONNECT request", (int)result); return result; } mqstate(data, MQTT_FIRST, MQTT_CONNACK); @@ -892,6 +892,24 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) break; } mq->npacket = 0; + /* PINGRESP and DISCONNECT must have remaining_length == 0 and + * reserved bits (low nibble) must be zero per MQTT 3.1.1 + * sections 2.2.2, 3.13.1 and 3.14.1. Reject before state + * dispatch to prevent nextstate confusion. */ + { + const unsigned char type = mq->firstbyte & 0xF0; + const unsigned char reserved = mq->firstbyte & 0x0F; + if((type == MQTT_MSG_DISCONNECT || type == MQTT_MSG_PINGRESP) && + (mq->remaining_length || reserved)) { + failf(data, + "Broker sent malformed %s " + "(remaining_length=%zu, header byte=0x%02x)", + type == MQTT_MSG_DISCONNECT ? "DISCONNECT" : "PINGRESP", + mq->remaining_length, mq->firstbyte); + result = CURLE_WEIRD_SERVER_REPLY; + break; + } + } if(mq->remaining_length) { mqstate(data, mqtt->nextstate, MQTT_NOSTATE); break; diff --git a/deps/curl/lib/multi.c b/deps/curl/lib/multi.c index 7520253d70..d6ae111d8e 100644 --- a/deps/curl/lib/multi.c +++ b/deps/curl/lib/multi.c @@ -78,8 +78,8 @@ /* On a debug build, we want to fail hard on multi handles that * are not NULL, but no longer have the MAGIC touch. This gives * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \ (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_MULTI_HANDLE(x) \ @@ -106,20 +106,56 @@ static const struct curltime *multi_now(struct Curl_multi *multi) return &multi->now; } -/* function pointer called once when switching TO a state */ -typedef void (*init_multistate_func)(struct Curl_easy *data); +/* function pointer called once when entering a state */ +typedef void (*mstate_enter_func)(struct Curl_easy *data, + CURLMstate from_state); + +static void mstate_enter_connect(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; + Curl_init_CONNECT(data); +} -/* called in DID state, before PERFORMING state */ -static void before_perform(struct Curl_easy *data) +static void mstate_enter_did(struct Curl_easy *data, + CURLMstate from_state) { + (void)from_state; data->req.chunk = FALSE; Curl_pgrsTime(data, TIMER_PRETRANSFER); + if(!CURL_REQ_WANT_SEND(data)) + Curl_pgrsTime(data, TIMER_POSTRANSFER); +} + +static void mstate_enter_done(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); } -static void init_completed(struct Curl_easy *data) +static void mstate_enter_completed(struct Curl_easy *data, + CURLMstate from_state) { - /* this is a completed transfer */ + /* we sometimes directly jump to COMPLETED, trigger things + * we then missed. */ + if(from_state < MSTATE_DID) { + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_pgrsTime(data, TIMER_POSTRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + } + Curl_pgrsCompleted(data); + if(from_state < MSTATE_DONE) + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); + /* changing to COMPLETED means it is in process and needs to go */ + DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid)); + Curl_uint32_bset_remove(&data->multi->process, data->mid); + Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */ + if(Curl_uint32_bset_empty(&data->multi->process)) { + /* free the transfer buffer when we have no more active transfers */ + multi_xfer_bufs_free(data->multi); + } /* Important: reset the conn pointer so that we do not point to memory that could be freed anytime */ Curl_detach_connection(data); @@ -134,23 +170,23 @@ static void mstate(struct Curl_easy *data, CURLMstate state ) { CURLMstate oldstate = data->mstate; - static const init_multistate_func finit[MSTATE_LAST] = { - NULL, /* INIT */ - NULL, /* PENDING */ - NULL, /* SETUP */ - Curl_init_CONNECT, /* CONNECT */ - NULL, /* CONNECTING */ - NULL, /* PROTOCONNECT */ - NULL, /* PROTOCONNECTING */ - NULL, /* DO */ - NULL, /* DOING */ - NULL, /* DOING_MORE */ - before_perform, /* DID */ - NULL, /* PERFORMING */ - NULL, /* RATELIMITING */ - NULL, /* DONE */ - init_completed, /* COMPLETED */ - NULL /* MSGSENT */ + static const mstate_enter_func state_enter[MSTATE_LAST] = { + NULL, /* INIT */ + NULL, /* PENDING */ + NULL, /* SETUP */ + mstate_enter_connect, /* CONNECT */ + NULL, /* CONNECTING */ + NULL, /* PROTOCONNECT */ + NULL, /* PROTOCONNECTING */ + NULL, /* DO */ + NULL, /* DOING */ + NULL, /* DOING_MORE */ + mstate_enter_did, /* DID */ + NULL, /* PERFORMING */ + NULL, /* RATELIMITING */ + mstate_enter_done, /* DONE */ + mstate_enter_completed, /* COMPLETED */ + NULL /* MSGSENT */ }; if(oldstate == state) @@ -166,32 +202,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state /* really switching state */ data->mstate = state; - switch(state) { - case MSTATE_DONE: - CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); - break; - case MSTATE_COMPLETED: - /* we sometimes directly jump to COMPLETED, trigger also a notification - * in that case. */ - if(oldstate < MSTATE_DONE) - CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); - /* changing to COMPLETED means it is in process and needs to go */ - DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid)); - Curl_uint32_bset_remove(&data->multi->process, data->mid); - Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */ - - if(Curl_uint32_bset_empty(&data->multi->process)) { - /* free the transfer buffer when we have no more active transfers */ - multi_xfer_bufs_free(data->multi); - } - break; - default: - break; - } - - /* if this state has an init-function, run it */ - if(finit[state]) - finit[state](data); + if(state_enter[state]) + state_enter[state](data, oldstate); } #ifndef DEBUGBUILD @@ -208,7 +220,7 @@ static void ph_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /* @@ -404,7 +416,7 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a removed easy right away. + * not have to reuse the `mid` from a removed easy right away. * Since uint_tbl and uint_bset are memory efficient, * regard less than 25% free as insufficient. * (for low capacities, e.g. multi_easy, 4 or less). */ @@ -420,7 +432,7 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, new_size = max_capacity; /* can not be larger than this */ } else { - /* make it a 64 multiple, since our bitsets frow by that and + /* make it a 64 multiple, since our bitsets grow by that and * small (easy_multi) grows to at least 64 on first resize. */ new_size = (((used + min_unused) + 63) / 64) * 64; } @@ -519,6 +531,8 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) /* set the easy handle */ multistate(data, MSTATE_INIT); + /* not yet passed INIT state */ + data->state.really_alive = FALSE; #ifdef USE_LIBPSL /* Do the same for PSL. */ @@ -558,12 +572,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; - mresult = multi_assess_wakeup(multi); - if(mresult) { - failf(data, "error enabling wakeup listening: %d", mresult); - return mresult; - } - CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -839,6 +847,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } Curl_wildcard_dtor(&data->wildcard); @@ -1139,7 +1153,9 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, /* The admin handle always listens on the wakeup socket when there * are transfers alive. */ if(data->multi && (data == data->multi->admin) && - data->multi->xfers_alive) { + data->multi->xfers_really_alive) { + CURL_TRC_M(data, "adding wakeup, %u xfers really alive", + data->multi->xfers_really_alive); result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); } #endif @@ -1192,7 +1208,8 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, break; default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + failf(data, "multi_getsock: unexpected multi state %d", + (int)data->mstate); DEBUGASSERT(0); break; } @@ -1201,7 +1218,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, if(result) { if(result == CURLE_OUT_OF_MEMORY) return CURLM_OUT_OF_MEMORY; - failf(data, "error determining pollset: %d", result); + failf(data, "error determining pollset: %d", (int)result); return CURLM_INTERNAL_ERROR; } @@ -1707,9 +1724,13 @@ CURLMcode curl_multi_wakeup(CURLM *m) */ static bool multi_ischanged(struct Curl_multi *multi, bool clear) { - bool retval = (bool)multi->recheckstate; - if(clear) - multi->recheckstate = FALSE; + bool retval = FALSE; + DEBUGASSERT(multi); + if(multi) { + retval = (bool)multi->recheckstate; + if(clear) + multi->recheckstate = FALSE; + } return retval; } @@ -1773,16 +1794,16 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) * stage DO state which (wrongly) was introduced to support FTP's second * connection. * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to - * DOING state there is more work to do! + * 'complete' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK + * (to DOING state when there is more work to do) */ -static CURLcode multi_do_more(struct Curl_easy *data, int *complete) +static CURLcode multi_do_more(struct Curl_easy *data, domore *complete) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - *complete = 0; + *complete = DOMORE_INCOMPLETE; if(conn->scheme->run->do_more) result = conn->scheme->run->do_more(data, complete); @@ -2000,9 +2021,9 @@ static CURLcode mspeed_check(struct Curl_easy *data) return CURLE_OK; } -static CURLMcode state_performing(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_performing(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { char *newurl = NULL; bool retry = FALSE; @@ -2139,9 +2160,9 @@ static CURLMcode state_performing(struct Curl_easy *data, return mresult; } -static CURLMcode state_do(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_do(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; @@ -2176,8 +2197,6 @@ static CURLMcode state_do(struct Curl_easy *data, /* Perform the protocol's DO action */ result = multi_do(data, &dophase_done); - /* When multi_do() returns failure, data->conn might be NULL! */ - if(!result) { if(!dophase_done) { #ifndef CURL_DISABLE_FTP @@ -2276,8 +2295,8 @@ static CURLMcode state_do(struct Curl_easy *data, return mresult; } -static CURLMcode state_ratelimiting(struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_ratelimiting(struct Curl_easy *data, + CURLcode *resultp) { CURLcode result = CURLE_OK; CURLMcode mresult = CURLM_OK; @@ -2301,9 +2320,9 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, return mresult; } -static CURLMcode state_connect(struct Curl_multi *multi, - struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_connect(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode *resultp) { /* Connect. We want to get a connection identifier filled in. This state can be entered from SETUP and from PENDING. */ @@ -2444,21 +2463,263 @@ static void handle_completed(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); Curl_uint32_bset_remove(&multi->pending, data->mid); Curl_uint32_bset_add(&multi->msgsent, data->mid); + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } --multi->xfers_alive; if(!multi->xfers_alive) multi_assess_wakeup(multi); } -static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_easy *data, - struct Curl_sigpipe_ctx *sigpipe_ctx) +static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) +{ + if(!data->state.really_alive) { + data->state.really_alive = TRUE; + ++data->multi->xfers_really_alive; + if(data->multi->xfers_really_alive == 1) { + CURLMcode mresult = multi_assess_wakeup(data->multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + } + } + + *result = Curl_pretransfer(data); + if(*result) + return CURLM_OK; + + /* after init, go SETUP */ + multistate(data, MSTATE_SETUP); + Curl_pgrsTime(data, TIMER_STARTOP); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_setup(struct Curl_easy *data) +{ + Curl_pgrsTime(data, TIMER_STARTSINGLE); + if(data->set.timeout) + Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + if(data->set.connecttimeout) + /* Since a connection might go to pending and back to CONNECT several + times before it actually takes off, we need to set the timeout once + in SETUP before we enter CONNECT the first time. */ + Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + + multistate(data, MSTATE_CONNECT); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_connecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) { bool connected; + + if(!data->conn) { + DEBUGASSERT(0); + *result = CURLE_FAILED_INIT; + return CURLM_OK; + } + if(!Curl_xfer_recv_is_paused(data)) { + *result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + if(connected && !*result) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } + multistate(data, MSTATE_PROTOCONNECT); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", (int)*result); + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; + } + } + return CURLM_OK; +} + +static CURLMcode multistate_protoconnect(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool protocol_connected = FALSE; + + if(!*result && data->conn->bits.reuse) { + /* ftp seems to hang when protoconnect on reused connection since we + * handle PROTOCONNECT in general inside the filters, it seems wrong to + * restart this on a reused connection. + */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + if(!*result) + *result = protocol_connect(data, &protocol_connected); + if(!*result && !protocol_connected) { + /* switch to waiting state */ + multistate(data, MSTATE_PROTOCONNECTING); + return CURLM_CALL_MULTI_PERFORM; + } + else if(!*result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; +} + +static CURLMcode multistate_protoconnecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + bool protocol_connected = FALSE; + + /* protocol-specific connect phase */ + *result = protocol_connecting(data, &protocol_connected); + if(!*result && protocol_connected) { + /* after the connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool dophase_done = FALSE; + + /* we continue DOING until the DO phase is complete */ + DEBUGASSERT(data->conn); + *result = protocol_doing(data, &dophase_done); + if(!*result) { + if(dophase_done) { + /* after DO, go DO_DONE or DO_MORE */ + multistate(data, data->conn->bits.do_more ? + MSTATE_DOING_MORE : MSTATE_DID); + return CURLM_CALL_MULTI_PERFORM; + } /* dophase_done */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing_more(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + domore control; + + /* + * When we are connected, DOING MORE and then go DID + */ + DEBUGASSERT(data->conn); + *result = multi_do_more(data, &control); + + if(!*result) { + if(control != DOMORE_INCOMPLETE) { + /* if DONE, advance to DO_DONE + if GOBACK, go back to DOING */ + multistate(data, control == DOMORE_DONE ? MSTATE_DID : MSTATE_DOING); + return CURLM_CALL_MULTI_PERFORM; + } + /* else + stay in DO_MORE */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_did(struct Curl_multi *multi, + struct Curl_easy *data) +{ + DEBUGASSERT(data->conn); + if(data->conn->bits.multiplex) + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* multiplexed */ + + /* Only perform the transfer if there is a good socket to work with. + Having both BAD is a signal to skip immediately to DONE */ + if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || + CONN_SOCK_IDX_VALID(data->conn->send_idx)) + multistate(data, MSTATE_PERFORMING); + else { +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch && + ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { + data->wildcard->state = CURLWC_DONE; + } +#endif + multistate(data, MSTATE_DONE); + } + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *presult) +{ + if(data->conn) { + CURLcode result; + + /* post-transfer command */ + result = multi_done(data, *presult, FALSE); + + /* allow a previously set error code take precedence */ + if(!(*presult)) + *presult = result; + } + +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch) { + if(data->wildcard->state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with MSTATE_INIT */ + multistate(data, MSTATE_INIT); + return CURLM_CALL_MULTI_PERFORM; + } + } +#endif + /* after we have DONE what we are supposed to do, go COMPLETED, and + it does not matter what the multi_done() returned! */ + multistate(data, MSTATE_COMPLETED); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) +{ CURLMcode mresult; CURLcode result = CURLE_OK; - int control; if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; @@ -2479,6 +2740,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { +#ifdef ENABLE_WAKEUP + /* Consume any pending wakeup signals before processing. + * This is necessary for event based processing. See #21547 */ + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); +#endif #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); #endif @@ -2517,217 +2783,63 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_INIT: /* Transitional state. init this transfer. A handle never comes back to this state. */ - result = Curl_pretransfer(data); - if(result) - break; - - /* after init, go SETUP */ - multistate(data, MSTATE_SETUP); - Curl_pgrsTime(data, TIMER_STARTOP); - FALLTHROUGH(); + mresult = multistate_init(data, &result); + break; case MSTATE_SETUP: /* Transitional state. Setup things for a new transfer. The handle can come back to this state on a redirect. */ - Curl_pgrsTime(data, TIMER_STARTSINGLE); - if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - if(data->set.connecttimeout) - /* Since a connection might go to pending and back to CONNECT several - times before it actually takes off, we need to set the timeout once - in SETUP before we enter CONNECT the first time. */ - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - - multistate(data, MSTATE_CONNECT); - FALLTHROUGH(); + mresult = multistate_setup(data); + break; case MSTATE_CONNECT: - mresult = state_connect(multi, data, &result); + mresult = multistate_connect(multi, data, &result); break; case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - if(!data->conn) { - DEBUGASSERT(0); - result = CURLE_FAILED_INIT; - break; - } - else if(!Curl_xfer_recv_is_paused(data)) { - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); - if(connected && !result) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - mresult = CURLM_CALL_MULTI_PERFORM; - multistate(data, MSTATE_PROTOCONNECT); - } - else if(result) { - /* failure detected */ - CURL_TRC_M(data, "connect failed -> %d", result); - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - break; - } - } + mresult = multistate_connecting(data, &stream_error, &result); break; case MSTATE_PROTOCONNECT: - if(!result && data->conn->bits.reuse) { - /* ftp seems to hang when protoconnect on reused connection since we - * handle PROTOCONNECT in general inside the filers, it seems wrong to - * restart this on a reused connection. - */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - break; - } - if(!result) - result = protocol_connect(data, &protocol_connected); - if(!result && !protocol_connected) { - /* switch to waiting state */ - multistate(data, MSTATE_PROTOCONNECTING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(!result) { - /* protocol connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnect(data, &stream_error, &result); break; case MSTATE_PROTOCONNECTING: /* protocol-specific connect phase */ - result = protocol_connecting(data, &protocol_connected); - if(!result && protocol_connected) { - /* after the connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(result) { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnecting(data, &stream_error, &result); break; case MSTATE_DO: - mresult = state_do(data, &stream_error, &result); + mresult = multistate_do(data, &stream_error, &result); break; case MSTATE_DOING: /* we continue DOING until the DO phase is complete */ - DEBUGASSERT(data->conn); - result = protocol_doing(data, &dophase_done); - if(!result) { - if(dophase_done) { - /* after DO, go DO_DONE or DO_MORE */ - multistate(data, data->conn->bits.do_more ? - MSTATE_DOING_MORE : MSTATE_DID); - mresult = CURLM_CALL_MULTI_PERFORM; - } /* dophase_done */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing(data, &stream_error, &result); break; case MSTATE_DOING_MORE: /* * When we are connected, DOING MORE and then go DID */ - DEBUGASSERT(data->conn); - result = multi_do_more(data, &control); - - if(!result) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ - multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* else - stay in DO_MORE */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing_more(data, &stream_error, &result); break; case MSTATE_DID: - DEBUGASSERT(data->conn); - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* multiplexed */ - - /* Only perform the transfer if there is a good socket to work with. - Having both BAD is a signal to skip immediately to DONE */ - if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || - CONN_SOCK_IDX_VALID(data->conn->send_idx)) - multistate(data, MSTATE_PERFORMING); - else { -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch && - ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard->state = CURLWC_DONE; - } -#endif - multistate(data, MSTATE_DONE); - } - mresult = CURLM_CALL_MULTI_PERFORM; + mresult = multistate_did(multi, data); break; case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - mresult = state_ratelimiting(data, &result); + mresult = multistate_ratelimiting(data, &result); break; case MSTATE_PERFORMING: - mresult = state_performing(data, &stream_error, &result); + mresult = multistate_performing(data, &stream_error, &result); break; case MSTATE_DONE: - /* this state is highly transient, so run another loop after this */ - mresult = CURLM_CALL_MULTI_PERFORM; - - if(data->conn) { - CURLcode res; - - /* post-transfer command */ - res = multi_done(data, result, FALSE); - - /* allow a previously set error code take precedence */ - if(!result) - result = res; - } - -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch) { - if(data->wildcard->state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again - with MSTATE_INIT */ - multistate(data, MSTATE_INIT); - break; - } - } -#endif - /* after we have DONE what we are supposed to do, go COMPLETED, and - it does not matter what the multi_done() returned! */ - multistate(data, MSTATE_COMPLETED); + mresult = multistate_done(data, &result); break; case MSTATE_COMPLETED: @@ -3897,8 +4009,7 @@ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, if(data->multi->xfer_buf && data->set.buffer_size > data->multi->xfer_buf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_buf); - data->multi->xfer_buf = NULL; + curlx_safefree(data->multi->xfer_buf); data->multi->xfer_buf_len = 0; } @@ -3950,8 +4061,7 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_ulbuf && data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_ulbuf); - data->multi->xfer_ulbuf = NULL; + curlx_safefree(data->multi->xfer_ulbuf); data->multi->xfer_ulbuf_len = 0; } @@ -3985,11 +4095,12 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, size_t blen, char **pbuf) { DEBUGASSERT(data); - DEBUGASSERT(data->multi); *pbuf = NULL; if(!data->multi) { - failf(data, "transfer has no multi handle"); - return CURLE_FAILED_INIT; + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + *pbuf = curlx_malloc(blen); + return *pbuf ? CURLE_OK : CURLE_OUT_OF_MEMORY; } if(data->multi->xfer_sockbuf_borrowed) { failf(data, "attempt to borrow xfer_sockbuf when already borrowed"); @@ -3998,8 +4109,7 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_sockbuf && blen > data->multi->xfer_sockbuf_len) { /* not large enough, get a new one */ - curlx_free(data->multi->xfer_sockbuf); - data->multi->xfer_sockbuf = NULL; + curlx_safefree(data->multi->xfer_sockbuf); data->multi->xfer_sockbuf_len = 0; } @@ -4019,11 +4129,16 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf) { - (void)buf; DEBUGASSERT(data); - DEBUGASSERT(data->multi); - DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); - data->multi->xfer_sockbuf_borrowed = FALSE; + if(!data->multi) { + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + curlx_free(buf); + } + else { + DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); + data->multi->xfer_sockbuf_borrowed = FALSE; + } } static void multi_xfer_bufs_free(struct Curl_multi *multi) @@ -4054,6 +4169,9 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) { + DEBUGASSERT(multi); + if(!multi) + return 0; return multi->xfers_alive; } diff --git a/deps/curl/lib/multi_ev.c b/deps/curl/lib/multi_ev.c index 937e7ce48d..0da5b0e904 100644 --- a/deps/curl/lib/multi_ev.c +++ b/deps/curl/lib/multi_ev.c @@ -40,6 +40,10 @@ static void mev_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } +#ifdef DEBUGBUILD +#define SH_ENTRY_MAGIC 0x570091d +#endif + /* Information about a socket for which we inform the libcurl application * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE) */ @@ -51,6 +55,9 @@ struct mev_sh_entry { * libcurl application to watch out for */ unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ +#ifdef DEBUGBUILD + unsigned int magic; +#endif BIT(announced); /* this socket has been passed to the socket callback at least once */ }; @@ -75,6 +82,9 @@ static void mev_sh_entry_dtor(void *freethis) { struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis; Curl_uint32_spbset_destroy(&entry->xfers); +#ifdef DEBUGBUILD + entry->magic = 0; +#endif curlx_free(entry); } @@ -113,7 +123,9 @@ static struct mev_sh_entry *mev_sh_entry_add(struct Curl_hash *sh, mev_sh_entry_dtor(check); return NULL; /* major failure */ } - +#ifdef DEBUGBUILD + check->magic = SH_ENTRY_MAGIC; +#endif return check; /* things are good in sockhash land */ } @@ -223,6 +235,7 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, /* we should only be called when the callback exists */ DEBUGASSERT(multi->socket_cb); + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); if(!multi->socket_cb) return CURLM_OK; @@ -272,12 +285,18 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->user_data); mev_in_callback(multi, FALSE); - entry->announced = TRUE; if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; } - entry->action = (unsigned int)comboaction; + /* curl_easy_pause() is documented as callable from any callback; it + * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + entry = mev_sh_entry_get(&multi->ev.sh_entries, s); + if(entry) { + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); + entry->announced = TRUE; + entry->action = (unsigned int)comboaction; + } return CURLM_OK; } @@ -486,9 +505,9 @@ static CURLMcode mev_assess(struct Curl_multi *multi, Curl_pollset_init(&ps); if(conn) { - CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); - if(r) { - mresult = (r == CURLE_OUT_OF_MEMORY) ? + CURLcode result = Curl_conn_adjust_pollset(data, conn, &ps); + if(result) { + mresult = (result == CURLE_OUT_OF_MEMORY) ? CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; goto out; } diff --git a/deps/curl/lib/multihandle.h b/deps/curl/lib/multihandle.h index c5cdfbe82e..19dd2ffcdf 100644 --- a/deps/curl/lib/multihandle.h +++ b/deps/curl/lib/multihandle.h @@ -85,6 +85,8 @@ struct Curl_multi { unsigned int xfers_alive; /* amount of added transfers that have not yet reached COMPLETE state */ + unsigned int xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ diff --git a/deps/curl/lib/netrc.c b/deps/curl/lib/netrc.c index 72d8feee7d..7c8e1fb2ab 100644 --- a/deps/curl/lib/netrc.c +++ b/deps/curl/lib/netrc.c @@ -36,88 +36,154 @@ #endif #include "netrc.h" +#include "urldata.h" +#include "creds.h" +#include "curl_trc.h" #include "strcase.h" #include "curl_get_line.h" #include "curlx/fopen.h" #include "curlx/strparse.h" -/* Get user and password from .netrc when given a machine name */ -enum host_lookup_state { - NOTHING, - HOSTFOUND, /* the 'machine' keyword was found */ - HOSTVALID, /* this is "our" machine! */ - MACDEF -}; - -enum found_state { - NONE, - LOGIN, - PASSWORD -}; - -#define FOUND_LOGIN 1 -#define FOUND_PASSWORD 2 +/* .netrc is not really a standard. The GNU definition can be found here: + * https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html + * This gives grammar like: + * + * LITERAL := \S+ | QUOTED + * QUOTED := "(\\[rnt\]|[^"])*" + * ANYTHING := . + * EMPTY_LINE := \r*\n\r*\n + * MACHINE := machine # case-insensitive + * LOGIN := login # case-insensitive + * PASSWD := password # case-insensitive + * ACCOUNT := account # case-insensitive + * MACDEF := macdef # case-insensitive + * DEFAULT := default # case-insensitive + * + * MACRO := MACDEF ANYTHING* EMPTY_LINE + * JUNK := LITERAL + * LKEY := ( LOGIN | PASSWD | ACCOUNT ) LITERAL + * MENTRY := MACHINE LITERAL LKEY* + * DENTRY := DEFAULT LKEY* + * NETRC := (MENTRY | DENTRY | MACRO | JUNK )* EOF + * + * Tokens are separated by whitespace or newlines. which have otherwise + * no special meaning, apart from the empty line ending a MACRO. + * + * Parsing is not strict, unmatched LITERALs are ignored + */ #define MAX_NETRC_LINE 16384 #define MAX_NETRC_FILE (128 * 1024) #define MAX_NETRC_TOKEN 4096 +#define NETRC_DEBUG 0 + /* convert a dynbuf call CURLcode error to a NETRCcode error */ -#define curl2netrc(result) \ - (((result) == CURLE_OUT_OF_MEMORY) ? \ - NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR) +#define curl2netrc(r) \ + ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \ + NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)) + +typedef enum { + NETRC_TOK_EOF, + NETRC_TOK_LITERAL, + NETRC_TOK_MACHINE, + NETRC_TOK_DEFAULT, + NETRC_TOK_ACCOUNT, + NETRC_TOK_LOGIN, + NETRC_TOK_PASSWD, + NETRC_TOK_MACDEF, + NETRC_TOK_JUNK +} curl_netrc_token; + +struct netrc_lexer { + struct Curl_easy *data; + const char *content; + const char *pos; + struct dynbuf literal; + curl_netrc_token token; + bool pushed; +}; -static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) +#if NETRC_DEBUG +static const char *netrc_tokenstr(curl_netrc_token token) { - NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ - FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + switch(token) { + case NETRC_TOK_EOF: + return "[EOF]"; + case NETRC_TOK_LITERAL: + return "[LITERAL]"; + case NETRC_TOK_MACHINE: + return "[MACHINE]"; + case NETRC_TOK_DEFAULT: + return "[DEFAULT]"; + case NETRC_TOK_ACCOUNT: + return "[ACCOUNT]"; + case NETRC_TOK_LOGIN: + return "[LOGIN]"; + case NETRC_TOK_PASSWD: + return "[PASSWORD]"; + case NETRC_TOK_MACDEF: + return "[MACDEF]"; + case NETRC_TOK_JUNK: + return "[JUNK]"; + default: + return "[???]"; + } +} +#endif - if(file) { - curlx_struct_stat stat; - if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { - CURLcode result = CURLE_OK; - bool eof; - struct dynbuf linebuf; - curlx_dyn_init(&linebuf, MAX_NETRC_LINE); - ret = NETRC_OK; - do { - const char *line; - /* Curl_get_line always returns lines ending with a newline */ - result = Curl_get_line(&linebuf, file, &eof); - if(!result) { - line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); - } - if(result) { - curlx_dyn_free(filebuf); - ret = curl2netrc(result); - break; - } - } while(!eof); - curlx_dyn_free(&linebuf); +static void netrc_lexer_init(struct netrc_lexer *lexer, + struct Curl_easy *data, + const char *content) +{ + curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN); + lexer->data = data; + lexer->content = lexer->pos = content; +} + +static void netrc_lexer_cleanup(struct netrc_lexer *lexer) +{ + lexer->content = lexer->pos = NULL; + lexer->data = NULL; + curlx_dyn_free(&lexer->literal); +} + +static void netrc_skip_blanks(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + curlx_str_passblanks(&s); + while(*s == '\r') + ++s; + if(*s == '\n') { + ++s; } - curlx_fclose(file); + else + break; } - return ret; + lexer->pos = s; } -/* bundled parser state to keep function signatures compact */ -struct netrc_state { - char *login; - char *password; - enum host_lookup_state state; - enum found_state keyword; - NETRCcode retcode; - unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ - bool our_login; - bool done; - bool specific_login; -}; +static void netrc_skip_to_empty_line(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + if(*s == '\r') + ++s; + else if(*s == '\n') { + ++s; + while(*s == '\r') + ++s; + if(*s == '\n') + goto out; + } + else + ++s; + } +out: + lexer->pos = s; +} /* * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t @@ -125,365 +191,379 @@ struct netrc_state { * * Returns NETRC_OK or error. */ -static NETRCcode netrc_quoted_token(const char **tok_endp, - struct dynbuf *token) +static NETRCcode netrc_lexer_quoted(struct netrc_lexer *lexer) { - bool escape = FALSE; NETRCcode rc = NETRC_SYNTAX_ERROR; - const char *tok_end = *tok_endp; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - CURLcode result; - char s = *tok_end; + const char *s = lexer->pos; + bool escape = FALSE; + CURLcode result; + + DEBUGASSERT(*s == '\"'); + ++s; /* pass the leading quote */ + while(*s) { + char c = *s; if(escape) { escape = FALSE; - switch(s) { + switch(c) { case 'n': - s = '\n'; + c = '\n'; break; case 'r': - s = '\r'; + c = '\r'; break; case 't': - s = '\t'; + c = '\t'; break; } } - else if(s == '\\') { + else if(c == '\\') { escape = TRUE; - tok_end++; + ++s; continue; } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ + else if(c == '\"') { + ++s; /* pass the ending quote */ rc = NETRC_OK; - break; + goto out; } - result = curlx_dyn_addn(token, &s, 1); + result = curlx_dyn_addn(&lexer->literal, &c, 1); if(result) { - *tok_endp = tok_end; - return curl2netrc(result); + rc = curl2netrc(result); + goto out; } - tok_end++; + ++s; } - *tok_endp = tok_end; +out: + lexer->pos = s; return rc; } -/* - * Gets the next token from the netrc buffer at *tokp. Writes the token into - * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input - * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE - * when the line is exhausted. - * - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_get_token(const char **tokp, - const char **tok_endp, - struct dynbuf *token, - enum host_lookup_state *statep, - bool *lineend) +static void netrc_lexer_push(struct netrc_lexer *lexer) { - const char *tok = *tokp; - const char *tok_end; - - *lineend = FALSE; - curlx_dyn_reset(token); - curlx_str_passblanks(&tok); - - /* tok is first non-space letter */ - if(*statep == MACDEF) { - if((*tok == '\n') || (*tok == '\r')) - *statep = NOTHING; /* end of macro definition */ - *lineend = TRUE; - *tokp = tok; - return NETRC_OK; - } + lexer->pushed = TRUE; +} - if(!*tok || (*tok == '\n')) { - /* end of line */ - *lineend = TRUE; - *tokp = tok; - return NETRC_OK; +static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer, + bool want_literal) +{ + const char *s = lexer->pos, *start; + NETRCcode rc = NETRC_OK; + size_t slen; + CURLcode result; + + if(lexer->pushed) { + lexer->pushed = FALSE; + goto out; } - tok_end = tok; - if(*tok == '\"') { - /* quoted string */ - NETRCcode ret = netrc_quoted_token(&tok_end, token); - if(ret) - return ret; - } - else { + curlx_dyn_reset(&lexer->literal); + netrc_skip_blanks(lexer); + s = lexer->pos; + + switch(*s) { + case 0: + lexer->token = NETRC_TOK_EOF; + break; + case '\"': + rc = netrc_lexer_quoted(lexer); + lexer->token = NETRC_TOK_LITERAL; + s = lexer->pos; + break; + default: /* unquoted token */ - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; + start = s; + while(*s && !ISBLANK(*s) && !ISNEWLINE(*s)) + ++s; + slen = s - start; + if(!slen) { + rc = NETRC_SYNTAX_ERROR; + } + if(want_literal) { + lexer->token = NETRC_TOK_LITERAL; + result = curlx_dyn_addn(&lexer->literal, start, slen); + rc = curl2netrc(result); + } + else if((slen == 7) && curl_strnequal(start, "machine", slen)) { + lexer->token = NETRC_TOK_MACHINE; + } + else if((slen == 7) && curl_strnequal(start, "default", slen)) { + lexer->token = NETRC_TOK_DEFAULT; + } + else if((slen == 7) && curl_strnequal(start, "account", slen)) { + lexer->token = NETRC_TOK_ACCOUNT; + } + else if((slen == 5) && curl_strnequal(start, "login", slen)) { + lexer->token = NETRC_TOK_LOGIN; + } + else if((slen == 8) && curl_strnequal(start, "password", slen)) { + lexer->token = NETRC_TOK_PASSWD; } - if(!len) - return NETRC_SYNTAX_ERROR; - result = curlx_dyn_addn(token, tok, len); - if(result) - return curl2netrc(result); + else if((slen == 6) && curl_strnequal(start, "macdef", slen)) { + lexer->token = NETRC_TOK_MACDEF; + } + else { + lexer->token = NETRC_TOK_JUNK; + } + break; } - *tok_endp = tok_end; +out: +#if NETRC_DEBUG + CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d", + netrc_tokenstr(lexer->token), + curlx_dyn_ptr(&lexer->literal), rc); +#endif + lexer->pos = s; + return rc; +} - if(curlx_dyn_len(token)) - *tokp = curlx_dyn_ptr(token); - else - /* set it to blank to avoid NULL */ - *tokp = ""; +struct netrc_scanner { + struct netrc_lexer lexer; + const char *hostname; /* non-NULL, machine to scan for */ + const char *user; /* maybe NULL, login to scan for */ + char *login; + char *passwd; + struct Curl_creds *creds; + bool matches_host; + bool found; +}; - return NETRC_OK; +static void netrc_scan_reset(struct netrc_scanner *sc) +{ + curlx_safefree(sc->login); + curlx_safefree(sc->passwd); + sc->matches_host = FALSE; } -/* - * Reset parser for a new machine entry. Frees password and optionally login - * if it was not user-specified. - */ -static void netrc_new_machine(struct netrc_state *ns) +static void netrc_scan_init(struct netrc_scanner *sc, + struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user) { - ns->keyword = NONE; - ns->found = 0; - ns->our_login = FALSE; - curlx_safefree(ns->password); - if(!ns->specific_login) - curlx_safefree(ns->login); + memset(sc, 0, sizeof(*sc)); + netrc_lexer_init(&sc->lexer, data, content); + sc->hostname = hostname; + sc->user = (user && user[0]) ? user : NULL; + netrc_scan_reset(sc); } -/* - * Process a parsed token through the HOSTVALID state machine branch. This - * handles login/password values and keyword transitions for the matched host. - * - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) +static void netrc_scan_cleanup(struct netrc_scanner *sc) { - if(ns->keyword == LOGIN) { - if(ns->specific_login) - ns->our_login = !Curl_timestrcmp(ns->login, tok); - else { - ns->our_login = TRUE; - curlx_free(ns->login); - ns->login = curlx_strdup(tok); - if(!ns->login) - return NETRC_OUT_OF_MEMORY; - } - ns->found |= FOUND_LOGIN; - ns->keyword = NONE; - } - else if(ns->keyword == PASSWORD) { - curlx_free(ns->password); - ns->password = curlx_strdup(tok); - if(!ns->password) - return NETRC_OUT_OF_MEMORY; - ns->found |= FOUND_PASSWORD; - ns->keyword = NONE; - } - else if(curl_strequal("login", tok)) - ns->keyword = LOGIN; - else if(curl_strequal("password", tok)) - ns->keyword = PASSWORD; - else if(curl_strequal("machine", tok)) { - /* a new machine here */ - - if(ns->found & FOUND_PASSWORD && - /* a password was provided for this host */ - - ((!ns->specific_login || ns->our_login) || - /* either there was no specific login to search for, or this - is the specific one we wanted */ - (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { - /* or we look for a specific login, but that was not specified */ - - ns->done = TRUE; - return NETRC_OK; - } - - ns->state = HOSTFOUND; - netrc_new_machine(ns); - } - else if(curl_strequal("default", tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; - netrc_new_machine(ns); - } - if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) - ns->done = TRUE; - return NETRC_OK; + netrc_scan_reset(sc); + sc->hostname = NULL; + sc->user = NULL; + Curl_creds_unlink(&sc->creds); + netrc_lexer_cleanup(&sc->lexer); } -/* - * Process one parsed token through the netrc state - * machine. Updates the parser state in *ns. - * Returns NETRC_OK or an error code. - */ -static NETRCcode netrc_handle_token(struct netrc_state *ns, - const char *tok, - const char *host) +static NETRCcode netrc_scan_literal(struct netrc_scanner *sc, + char **pdest) { - switch(ns->state) { - case NOTHING: - if(curl_strequal("macdef", tok)) - ns->state = MACDEF; - else if(curl_strequal("machine", tok)) { - ns->state = HOSTFOUND; - netrc_new_machine(ns); - } - else if(curl_strequal("default", tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; - } - break; - case MACDEF: - if(!*tok) - ns->state = NOTHING; - break; - case HOSTFOUND: - if(curl_strequal(host, tok)) { - ns->state = HOSTVALID; - ns->retcode = NETRC_OK; + NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE); + if(!rc) { + if(sc->lexer.token == NETRC_TOK_LITERAL) { + if(pdest && sc->matches_host) { + curlx_free(*pdest); + *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal)); + if(!*pdest) + rc = NETRC_OUT_OF_MEMORY; + } } else - ns->state = NOTHING; - break; - case HOSTVALID: - return netrc_hostvalid(ns, tok); + netrc_lexer_push(&sc->lexer); } - return NETRC_OK; + return rc; } -/* - * Finalize the parse result: fill in defaults and free - * resources on error. - */ -static NETRCcode netrc_finalize(struct netrc_state *ns, - char **loginp, - char **passwordp, - struct store_netrc *store) +static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc) { - NETRCcode retcode = ns->retcode; - if(!retcode) { - if(!ns->password && ns->our_login) { - /* success without a password, set a blank one */ - ns->password = curlx_strdup(""); - if(!ns->password) - retcode = NETRC_OUT_OF_MEMORY; + NETRCcode rc = NETRC_OK; +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, + "[NETRC] entry matches_host=%d, login='%s', passwd='%s'", + sc->matches_host, sc->login, sc->passwd); +#endif + if(sc->matches_host) { + if(sc->login) { + if(sc->user) { + if(Curl_timestrcmp(sc->user, sc->login)) + goto out; + /* We look for a specific user, + * entry is only interesting with password */ + sc->found = !!sc->passwd; + } + else { + sc->found = TRUE; + } + } + else if(sc->passwd) { + /* found a passwd that applies to any user */ + sc->found = TRUE; + } + else { + /* entry has nothing interesting */ + } + if(sc->found) { +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found"); +#endif + if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd, + NULL, NULL, NULL, CREDS_NETRC, &sc->creds)) + rc = NETRC_OUT_OF_MEMORY; } - else if(!ns->login && !ns->password) - /* a default with no credentials */ - retcode = NETRC_NO_MATCH; - } - if(!retcode) { - /* success */ - if(!ns->specific_login) - *loginp = ns->login; - - /* netrc_finalize() can return a password even when specific_login is set - but our_login is false (e.g., host matched but the requested login - never matched). See test 685. */ - *passwordp = ns->password; - } - else { - curlx_dyn_free(&store->filebuf); - store->loaded = FALSE; - if(!ns->specific_login) - curlx_free(ns->login); - curlx_free(ns->password); } - return retcode; +out: + netrc_scan_reset(sc); + return rc; } -/* - * Returns zero on success. - */ -static NETRCcode parsenetrc(struct store_netrc *store, - const char *host, - char **loginp, - char **passwordp, - const char *netrcfile) +static NETRCcode netrc_scan(struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user, + struct Curl_creds **pcreds) { - const char *netrcbuffer; - struct dynbuf token; - struct dynbuf *filebuf = &store->filebuf; - struct netrc_state ns; - - memset(&ns, 0, sizeof(ns)); - ns.retcode = NETRC_NO_MATCH; - ns.login = *loginp; - ns.specific_login = !!ns.login; - - DEBUGASSERT(!*passwordp); - curlx_dyn_init(&token, MAX_NETRC_TOKEN); - - if(!store->loaded) { - NETRCcode ret = file2memory(netrcfile, filebuf); - if(ret) - return ret; - store->loaded = TRUE; - } - - netrcbuffer = curlx_dyn_ptr(filebuf); - - while(!ns.done) { - const char *tok = netrcbuffer; - while(tok && !ns.done) { - const char *tok_end; - bool lineend; - NETRCcode ret; - - ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); - if(ret) { - ns.retcode = ret; - goto out; - } - if(lineend) + struct netrc_scanner sc; + NETRCcode rc = NETRC_OK; + + Curl_creds_unlink(pcreds); + netrc_scan_init(&sc, data, content, hostname, user); + + while(!rc && !sc.found) { + rc = netrc_lexer_next(&sc.lexer, FALSE); + if(!rc) { + /* Does this token end any previous entry? */ + switch(sc.lexer.token) { + case NETRC_TOK_EOF: + case NETRC_TOK_MACHINE: + case NETRC_TOK_DEFAULT: + case NETRC_TOK_MACDEF: + rc = netrc_scan_end_entry(&sc); + if(rc || sc.found) + goto out; break; + default: + break; + } - ret = netrc_handle_token(&ns, tok, host); - if(ret) { - ns.retcode = ret; + switch(sc.lexer.token) { + case NETRC_TOK_EOF: goto out; - } - /* tok_end cannot point to a null byte here since lines are always - newline terminated */ - DEBUGASSERT(*tok_end); - tok = ++tok_end; - } - if(!ns.done) { - const char *nl = NULL; - if(tok) - nl = strchr(tok, '\n'); - if(!nl) + case NETRC_TOK_MACHINE: + rc = netrc_lexer_next(&sc.lexer, TRUE); + if(!rc) { + if(sc.lexer.token == NETRC_TOK_LITERAL) { + sc.matches_host = curl_strequal( + sc.hostname, curlx_dyn_ptr(&sc.lexer.literal)); + } + else { + sc.matches_host = FALSE; + netrc_lexer_push(&sc.lexer); + } + } + break; + case NETRC_TOK_DEFAULT: + sc.matches_host = TRUE; + break; + case NETRC_TOK_ACCOUNT: + rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */ + break; + case NETRC_TOK_LOGIN: + rc = netrc_scan_literal(&sc, &sc.login); break; - /* point to next line */ - netrcbuffer = &nl[1]; + case NETRC_TOK_PASSWD: + rc = netrc_scan_literal(&sc, &sc.passwd); + break; + case NETRC_TOK_MACDEF: + netrc_skip_to_empty_line(&sc.lexer); + break; + case NETRC_TOK_LITERAL: + case NETRC_TOK_JUNK: + default: + /* skip this */ + break; + } } - } /* while !done */ + } out: - curlx_dyn_free(&token); - return netrc_finalize(&ns, loginp, passwordp, store); + if(!rc) { + if(sc.creds) + Curl_creds_link(pcreds, sc.creds); + else + rc = NETRC_NO_MATCH; + } + netrc_scan_cleanup(&sc); + return rc; } -const char *Curl_netrc_strerror(NETRCcode ret) +static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) { - switch(ret) { - default: - return ""; /* not a legit error */ - case NETRC_FILE_MISSING: - return "no such file"; - case NETRC_NO_MATCH: - return "no matching entry"; - case NETRC_OUT_OF_MEMORY: - return "out of memory"; - case NETRC_SYNTAX_ERROR: - return "syntax error"; + NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ + FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + + curlx_dyn_reset(filebuf); + if(file) { + curlx_struct_stat stat; + if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + CURLcode result = CURLE_OK; + bool eof; + struct dynbuf linebuf; + curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + ret = NETRC_OK; + do { + const char *line; + /* Curl_get_line always returns lines ending with a newline */ + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } + if(result) { + curlx_dyn_free(filebuf); + ret = curl2netrc(result); + break; + } + } while(!eof); + curlx_dyn_free(&linebuf); + } + curlx_fclose(file); } - /* never reached */ + return ret; +} + +static NETRCcode netrc_scan_file(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds) +{ + struct dynbuf *filebuf = &store->filebuf; + + if(!store->loaded || strcmp(netrcfile, store->filename)) { + NETRCcode ret; + store->loaded = FALSE; + ret = file2memory(netrcfile, filebuf); + if(ret) { + CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile); + return ret; + } + curlx_free(store->filename); + store->filename = curlx_strdup(netrcfile); + if(!store->filename) { + curlx_dyn_reset(&store->filebuf); + return NETRC_OUT_OF_MEMORY; + } + store->loaded = TRUE; + } + + return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds); } /* @@ -492,16 +572,22 @@ const char *Curl_netrc_strerror(NETRCcode ret) * *loginp and *passwordp MUST be allocated if they are not NULL when passed * in. */ -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile) +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; - char *filealloc = NULL; + CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'", + netrcfile, hostname, user); + Curl_creds_unlink(pcreds); if(!netrcfile) { char *home = NULL; char *homea = NULL; + char *filealloc = NULL; #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) char pwbuf[1024]; #endif @@ -543,10 +629,12 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { curlx_free(homea); - return NETRC_OUT_OF_MEMORY; + retcode = NETRC_OUT_OF_MEMORY; + goto out; } } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -556,14 +644,20 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, curlx_free(homea); return NETRC_OUT_OF_MEMORY; } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); } #endif curlx_free(homea); } else - retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); + retcode = netrc_scan_file( + data, store, hostname, user, netrcfile, pcreds); + +out: + if(retcode) + Curl_creds_unlink(pcreds); return retcode; } @@ -571,10 +665,30 @@ void Curl_netrc_init(struct store_netrc *store) { curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); store->loaded = FALSE; + store->filename = NULL; } void Curl_netrc_cleanup(struct store_netrc *store) { curlx_dyn_free(&store->filebuf); + curlx_safefree(store->filename); store->loaded = FALSE; } -#endif + +const char *Curl_netrc_strerror(NETRCcode ret) +{ + switch(ret) { + default: + return ""; /* not a legit error */ + case NETRC_FILE_MISSING: + return "no such file"; + case NETRC_NO_MATCH: + return "no matching entry"; + case NETRC_OUT_OF_MEMORY: + return "out of memory"; + case NETRC_SYNTAX_ERROR: + return "syntax error"; + } + /* never reached */ +} + +#endif /* !CURL_DISABLE_NETRC */ diff --git a/deps/curl/lib/netrc.h b/deps/curl/lib/netrc.h index 90318c2bd6..6be9b83316 100644 --- a/deps/curl/lib/netrc.h +++ b/deps/curl/lib/netrc.h @@ -29,6 +29,9 @@ #include "curlx/dynbuf.h" +struct Curl_easy; +struct Curl_creds; + struct store_netrc { struct dynbuf filebuf; char *filename; @@ -48,14 +51,14 @@ const char *Curl_netrc_strerror(NETRCcode ret); void Curl_netrc_init(struct store_netrc *store); void Curl_netrc_cleanup(struct store_netrc *store); -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile); -/* Assume: (*passwordp)[0]=0, host[0] != 0. - * If (*loginp)[0] = 0, search for login and password within a machine - * section in the netrc. - * If (*loginp)[0] != 0, search for password within machine and login. - */ +/* Scan a netrc file for credentials matching hostname + * and optional user. */ +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds); #else /* disabled */ #define Curl_netrc_init(x) diff --git a/deps/curl/lib/noproxy.c b/deps/curl/lib/noproxy.c deleted file mode 100644 index 05c59a0f1a..0000000000 --- a/deps/curl/lib/noproxy.c +++ /dev/null @@ -1,266 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#ifndef CURL_DISABLE_PROXY - -#include "curlx/inet_pton.h" -#include "noproxy.h" -#include "curlx/strparse.h" - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_ARPA_INET_H -#include -#endif - -/* - * cidr4_match() returns TRUE if the given IPv4 address is within the - * specified CIDR address range. - * - * @unittest 1614 - */ -UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits) -{ - unsigned int address = 0; - unsigned int check = 0; - - if(bits > 32) - /* strange input */ - return FALSE; - - if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) - return FALSE; - if(curlx_inet_pton(AF_INET, network, &check) != 1) - return FALSE; - - if(bits && (bits != 32)) { - unsigned int mask = 0xffffffff << (32 - bits); - unsigned int haddr = htonl(address); - unsigned int hcheck = htonl(check); -#if 0 - curl_mfprintf(stderr, "Host %s (%x) network %s (%x) " - "bits %u mask %x => %x\n", - ipv4, haddr, network, hcheck, bits, mask, - (haddr ^ hcheck) & mask); -#endif - if((haddr ^ hcheck) & mask) - return FALSE; - return TRUE; - } - return address == check; -} - -/* @unittest 1614 */ -UNITTEST bool cidr6_match(const char *ipv6, const char *network, - unsigned int bits); -UNITTEST bool cidr6_match(const char *ipv6, const char *network, - unsigned int bits) -{ -#ifdef USE_IPV6 - unsigned int bytes; - unsigned int rest; - unsigned char address[16]; - unsigned char check[16]; - - if(!bits) - bits = 128; - - bytes = bits / 8; - rest = bits & 0x07; - if((bytes > 16) || ((bytes == 16) && rest)) - return FALSE; - if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) - return FALSE; - if(curlx_inet_pton(AF_INET6, network, check) != 1) - return FALSE; - if(bytes && memcmp(address, check, bytes)) - return FALSE; - if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) - return FALSE; - - return TRUE; -#else - (void)ipv6; - (void)network; - (void)bits; - return FALSE; -#endif -} - -enum nametype { - TYPE_HOST, - TYPE_IPV4, - TYPE_IPV6 -}; - -static bool match_host(const char *token, size_t tokenlen, - const char *name, size_t namelen) -{ - bool match = FALSE; - - /* ignore trailing dots in the token to check */ - if(token[tokenlen - 1] == '.') - tokenlen--; - - if(tokenlen && (*token == '.')) { - /* ignore leading token dot as well */ - token++; - tokenlen--; - } - /* A: example.com matches 'example.com' - B: www.example.com matches 'example.com' - C: nonexample.com DOES NOT match 'example.com' - */ - if(tokenlen == namelen) - /* case A, exact match */ - match = curl_strnequal(token, name, namelen); - else if(tokenlen < namelen) { - /* case B, tailmatch domain */ - match = (name[namelen - tokenlen - 1] == '.') && - curl_strnequal(token, name + (namelen - tokenlen), tokenlen); - } - /* case C passes through, not a match */ - return match; -} - -static bool match_ip(int type, const char *token, size_t tokenlen, - const char *name) -{ - char *slash; - unsigned int bits = 0; - char checkip[128]; - if(tokenlen >= sizeof(checkip)) - /* this cannot match */ - return FALSE; - /* copy the check name to a temp buffer */ - memcpy(checkip, token, tokenlen); - checkip[tokenlen] = 0; - - slash = strchr(checkip, '/'); - /* if the slash is part of this token, use it */ - if(slash) { - curl_off_t value; - const char *p = &slash[1]; - if(curlx_str_number(&p, &value, 128) || *p) - return FALSE; - /* a too large value is rejected in the cidr function below */ - bits = (unsigned int)value; - *slash = 0; /* null-terminate there */ - } - if(type == TYPE_IPV6) - return cidr6_match(name, checkip, bits); - else - return cidr4_match(name, checkip, bits); -} - -/**************************************************************** - * Checks if the host is in the noproxy list. returns TRUE if it matches and - * therefore the proxy should NOT be used. - ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy) -{ - /* - * If we do not have a hostname at all, like for example with a FILE - * transfer, we have nothing to interrogate the noproxy list with. - */ - if(!name || name[0] == '\0') - return FALSE; - - /* no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - */ - if(no_proxy && no_proxy[0]) { - const char *p = no_proxy; - size_t namelen; - char address[16]; - enum nametype type = TYPE_HOST; - if(!strcmp("*", no_proxy)) - return TRUE; - - /* NO_PROXY was specified and it was not only an asterisk */ - - /* Check if name is an IP address; if not, assume it being a hostname. */ - namelen = strlen(name); - if(curlx_inet_pton(AF_INET, name, &address) == 1) - type = TYPE_IPV4; -#ifdef USE_IPV6 - else if(curlx_inet_pton(AF_INET6, name, &address) == 1) - type = TYPE_IPV6; -#endif - else { - /* ignore trailing dots in the hostname */ - if(name[namelen - 1] == '.') - namelen--; - } - - while(*p) { - const char *token; - size_t tokenlen = 0; - - /* pass blanks */ - curlx_str_passblanks(&p); - - token = p; - /* pass over the pattern */ - while(*p && !ISBLANK(*p) && (*p != ',')) { - p++; - tokenlen++; - } - - if(tokenlen) { - bool match = FALSE; - if(type == TYPE_HOST) - match = match_host(token, tokenlen, name, namelen); - else - match = match_ip(type, token, tokenlen, name); - - if(match) - return TRUE; - } - - /* pass blanks after pattern */ - curlx_str_passblanks(&p); - /* if not a comma, this ends the loop */ - if(*p != ',') - break; - /* pass any number of commas */ - while(*p == ',') - p++; - } /* while(*p) */ - } /* NO_PROXY was specified and it was not only an asterisk */ - - return FALSE; -} - -#endif /* CURL_DISABLE_PROXY */ diff --git a/deps/curl/lib/openldap.c b/deps/curl/lib/openldap.c index 48bf5b746d..8ec6bb27cd 100644 --- a/deps/curl/lib/openldap.c +++ b/deps/curl/lib/openldap.c @@ -345,9 +345,9 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(data->state.aptr.user) { - binddn = conn->user; - passwd.bv_val = conn->passwd; + if(conn->creds) { + binddn = Curl_creds_user(conn->creds); + passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds)); passwd.bv_len = strlen(passwd.bv_val); } @@ -355,7 +355,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) NULL, NULL, &li->msgid); if(rc != LDAP_SUCCESS) return oldap_map_error(rc, - data->state.aptr.user ? + data->state.creds ? CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND); oldap_state(data, li, newstate); return CURLE_OK; @@ -499,7 +499,7 @@ static Sockbuf_IO ldapsb_tls = { static bool ssl_installed(struct connectdata *conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - return li && li->recv != NULL; + return li && li->recv; } static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) @@ -617,8 +617,8 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) hosturl = curl_maprintf("%s://%s:%d", conn->scheme->name, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name, - conn->remote_port); + data->state.up.hostname : conn->origin->hostname, + conn->origin->port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -900,7 +900,8 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) result = oldap_perform_bind(data, OLDAP_BIND); break; } - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) break; FALLTHROUGH(); @@ -911,7 +912,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) else if(ssl_installed(conn)) { if(li->sasl.prefmech != SASL_AUTH_NONE) result = oldap_perform_mechs(data); - else if(data->state.aptr.user) + else if(data->state.creds) result = oldap_perform_bind(data, OLDAP_BIND); else { /* Version 3 supported: no bind required */ @@ -1177,7 +1178,7 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, binary = bv.bv_len > 7 && curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7); - for(i = 0; bvals[i].bv_val != NULL; i++) { + for(i = 0; bvals[i].bv_val; i++) { bool binval = FALSE; result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, diff --git a/deps/curl/lib/optiontable.pl b/deps/curl/lib/optiontable.pl index db0659cbb0..de6fb55151 100755 --- a/deps/curl/lib/optiontable.pl +++ b/deps/curl/lib/optiontable.pl @@ -39,7 +39,7 @@ HEAD ; -my $lastnum=0; +my $lastnum = 0; my %opt; my %type; @@ -47,7 +47,7 @@ my %alias; sub add { - my($optstr, $typestr, $num)=@_; + my($optstr, $typestr, $num) = @_; my $name; # remove all spaces from the type $typestr =~ s/ //g; @@ -59,7 +59,7 @@ sub add { } if($optstr =~ /^CURLOPT_(.*)/) { - $name=$1; + $name = $1; } $ext =~ s/CURLOPTTYPE_//; $ext =~ s/CBPOINT/CBPTR/; @@ -89,7 +89,7 @@ sub add { $fl .= $1; # the end - my @p=split(/, */, $fl); + my @p = split(/, */, $fl); add($p[0], $p[1], $p[2]); undef $fl; } @@ -106,14 +106,14 @@ sub add { } if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) { - my($opt, $type, $num)=($1,$2,$3); + my($opt, $type, $num) = ($1, $2, $3); add($opt, $type, $num); } # alias for an older option # old = new if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) { - my ($o, $n)=($1, $2); + my ($o, $n) = ($1, $2); # skip obsolete ones if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) { $o =~ s/^CURLOPT_//; diff --git a/deps/curl/lib/parsedate.c b/deps/curl/lib/parsedate.c index ce358bd465..b19423169a 100644 --- a/deps/curl/lib/parsedate.c +++ b/deps/curl/lib/parsedate.c @@ -98,8 +98,12 @@ const char * const Curl_month[] = { #ifndef CURL_DISABLE_PARSEDATE +#if SIZEOF_TIME_T < 5 #define PARSEDATE_LATER 1 +#endif +#if SIZEOF_TIME_T < 5 || defined(HAVE_TIME_T_UNSIGNED) #define PARSEDATE_SOONER 2 +#endif static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" diff --git a/deps/curl/lib/peer.c b/deps/curl/lib/peer.c new file mode 100644 index 0000000000..44bcb52f23 --- /dev/null +++ b/deps/curl/lib/peer.c @@ -0,0 +1,740 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * IDN conversions + */ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_IPHLPAPI_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) +#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) +#include /* workaround for old mingw-w64 missing to include it */ +#endif +#include +#endif + +#include "curl_addrinfo.h" +#include "curl_trc.h" +#include "protocol.h" +#include "http_proxy.h" +#include "idn.h" +#include "curlx/strdup.h" +#include "curlx/strparse.h" +#include "peer.h" +#include "urldata.h" +#include "url.h" +#include "vtls/vtls.h" + +struct peer_parse { + const struct Curl_scheme *scheme; + struct Curl_str host_user; + struct Curl_str host; + struct Curl_str zoneid; + char *tmp_host_user; + char *tmp_host; + char *tmp_zoneid; + uint32_t scopeid; + uint16_t port; + bool ipv6; + bool unix_socket; + bool abstract_uds; +}; + +static void peer_parse_clear(struct peer_parse *pp) +{ + curlx_free(pp->tmp_host_user); + curlx_free(pp->tmp_host); + curlx_free(pp->tmp_zoneid); + memset(pp, 0, sizeof(*pp)); +} + +static CURLcode peer_create(struct peer_parse *pp, + struct Curl_peer **ppeer) +{ + struct Curl_peer *peer = NULL; + CURLcode result = CURLE_OK; + size_t zone_alen = 0, host_alen = 0; + + if(!pp || !pp->scheme) + return CURLE_FAILED_INIT; + if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK)) + return CURLE_FAILED_INIT; + + if((pp->host.str != pp->host_user.str) || + (pp->host.len != pp->host_user.len)) { + host_alen = pp->host.len + 1; + } + zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; + + /* null-terminator already part of struct */ + peer = curlx_calloc(1, sizeof(*peer) + + pp->host_user.len + host_alen + zone_alen); + if(!peer) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + peer->refcount = 1; + peer->scheme = pp->scheme; + peer->hostname = peer->user_hostname; + peer->port = pp->port; + peer->scopeid = pp->scopeid; + peer->ipv6 = pp->ipv6; + peer->unix_socket = pp->unix_socket; + peer->abstract_uds = pp->abstract_uds; + + if(pp->host_user.len) + memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len); + + if(host_alen) { + peer->hostname = peer->user_hostname + pp->host_user.len + 1; + memcpy(peer->hostname, pp->host.str, pp->host.len); + } + + if(zone_alen) { + peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen; + memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len); +#ifdef USE_IPV6 + /* Determine scope_id if not already provided */ + if(!peer->scopeid) { + const char *p = peer->zoneid; + curl_off_t scope; + if(!curlx_str_number(&p, &scope, UINT_MAX)) { + /* A plain number, use it directly as a scope id. */ + peer->scopeid = (uint32_t)scope; + } +#ifdef HAVE_IF_NAMETOINDEX + else { + /* Zone identifier is not numeric */ + unsigned int idx = 0; + idx = if_nametoindex(peer->zoneid); + if(idx) { + peer->scopeid = (uint32_t)idx; + } + else { + /* Do we want to return an error here? */ + } + } +#endif /* HAVE_IF_NAMETOINDEX */ + } +#endif /* USE_IPV6 */ + } + +out: + if(!result) + *ppeer = peer; + else + Curl_peer_unlink(&peer); + return result; +} + +static CURLcode peer_parse_host(struct Curl_easy *data, + struct peer_parse *pp, + bool scan_for_ipv6) +{ + if(!pp || !pp->host_user.str || !pp->host_user.len) + return CURLE_FAILED_INIT; + + if(pp->host_user.str[0] == '[') { + const char *s = pp->host_user.str + 1; + struct Curl_str tmp; + if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']')) + return CURLE_URL_MALFORMAT; + + if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE, + &pp->host, &pp->zoneid)) { + failf(data, "Invalid IPv6 address format in '%.*s'", + (int)pp->host_user.len, pp->host_user.str); + return CURLE_URL_MALFORMAT; + } + pp->ipv6 = TRUE; + } + else { +#ifdef USE_IDN + if(!Curl_is_ASCII_str(&pp->host_user)) { + CURLcode result; + if(!pp->tmp_host_user) { + /* need a null-terminated string for IDN */ + pp->tmp_host_user = curlx_memdup0(pp->host_user.str, + pp->host_user.len); + if(!pp->tmp_host_user) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host); + if(result) + return result; + pp->host.str = pp->tmp_host; + pp->host.len = strlen(pp->host.str); + } + else +#endif + if(scan_for_ipv6 && + Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, + &pp->host, &pp->zoneid)) { + if(pp->host_user.len < MAX_IPADR_LEN) { + char tmp[MAX_IPADR_LEN]; + memcpy(tmp, pp->host_user.str, pp->host_user.len); + tmp[pp->host_user.len] = 0; + pp->ipv6 = !Curl_is_ipv4addr(tmp); + } + else + pp->ipv6 = TRUE; + } + else + pp->host = pp->host_user; + } + return CURLE_OK; +} + +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.scheme = scheme; + pp.host_user.str = hostname; + pp.host_user.len = strlen(hostname); + pp.port = port; + + result = peer_parse_host(data, &pp, TRUE); + if(!result) + result = peer_create(&pp, ppeer); + + peer_parse_clear(&pp); + return result; +} + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + size_t pathlen = path ? strlen(path) : 0; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!scheme) + return CURLE_FAILED_INIT; + if(!pathlen) + return CURLE_FAILED_INIT; + + pp.scheme = scheme; + pp.host_user.str = pp.host.str = path; + pp.host_user.len = pp.host.len = pathlen; + pp.unix_socket = TRUE; + pp.abstract_uds = abstract_unix_socket; + + result = peer_create(&pp, ppeer); + peer_parse_clear(&pp); + return result; +} +#endif /* USE_UNIX_SOCKETS */ + +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src) +{ + if(*pdest != src) { + Curl_peer_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_peer_unlink(struct Curl_peer **ppeer) +{ + if(*ppeer) { + struct Curl_peer *peer = *ppeer; + + DEBUGASSERT(peer->refcount); + *ppeer = NULL; + if(peer->refcount) + peer->refcount--; + if(!peer->refcount) { + curlx_free(peer); + } + } +} + +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->scheme == p2->scheme) && + Curl_peer_same_destination(p1, p2)); +} + +static bool peer_same_hostname(struct Curl_peer *p1, struct Curl_peer *p2) +{ + /* UNIX domain socket paths must be compared case-sensitive, + * as many filesystem are like that. */ + return (p1->unix_socket == p2->unix_socket) && + (p1->abstract_uds == p2->abstract_uds) && + (p1->ipv6 == p2->ipv6) && + (p1->unix_socket ? + !strcmp(p1->hostname, p2->hostname) : + curl_strequal(p1->hostname, p2->hostname)); +} + +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->port == p2->port) && + peer_same_hostname(p1, p2) && + (p1->scopeid == p2->scopeid) && + (p1->scopeid || curl_strequal(p1->zoneid, p2->zoneid))); +} + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + char *zoneid = NULL; + CURLUcode uc; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + + curlx_safefree(up->scheme); + uc = curl_url_get(uh, CURLUPART_SCHEME, &up->scheme, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + pp.scheme = Curl_get_scheme(up->scheme); + if(!pp.scheme) { + failf(data, "Protocol \"%s\" not supported%s", up->scheme, + data->state.this_is_a_follow ? " (in redirect)" : ""); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + + curlx_safefree(up->hostname); + uc = curl_url_get(uh, CURLUPART_HOST, &up->hostname, 0); + if(uc) { + if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) + ; /* acceptable */ + else { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(strlen(up->hostname) > MAX_URL_LEN) { + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); + result = CURLE_URL_MALFORMAT; + goto out; + } + + pp.host_user.str = up->hostname ? up->hostname : ""; + pp.host_user.len = strlen(pp.host_user.str); + if(pp.host_user.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + else + pp.host = pp.host_user; + + curlx_safefree(up->port); + if(port_override) { + /* if set, we use this instead of the port possibly given in the URL */ + char portbuf[16]; + curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override); + uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else + pp.port = port_override; + } + else { + uc = curl_url_get(uh, CURLUPART_PORT, &up->port, CURLU_DEFAULT_PORT); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) { + result = CURLE_URL_MALFORMAT; + goto out; + } + /* no port ok when not a network scheme */ + } + else { + const char *p = up->port; + curl_off_t offt; + if(curlx_str_number(&p, &offt, 0xffff)) + return CURLE_URL_MALFORMAT; + pp.port = (uint16_t)offt; + } + } + + if(scopeid_override) + /* Override any scope id from an URL zone. */ + pp.scopeid = scopeid_override; + else { + if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) == + CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(zoneid) { + pp.zoneid.str = zoneid; + pp.zoneid.len = strlen(zoneid); + } + } + + result = peer_create(&pp, ppeer); + if(result) + failf(data, "Error %d creating peer for %s:%u", + (int)result, pp.host_user.str, pp.port); + +out: + peer_parse_clear(&pp); + curlx_free(zoneid); + return result; +} + +/* Parse a "host:port" string to connect to into a peer. + * IPv6 addresses might appear in brackets or without them. */ +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + const char *portstr = NULL; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!connect_to || !*connect_to) + return CURLE_FAILED_INIT; + + pp.scheme = dest->scheme; + + /* detect and extract RFC6874-style IPv6-addresses */ + if(connect_to[0] == '[') { + const char *s = strchr(connect_to + 1, ']'); + if(!s) { + failf(data, "Invalid IPv6 address format in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + portstr = strchr(s, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = s - pp.host_user.str + 1; + pp.ipv6 = TRUE; + } + else { + portstr = strchr(connect_to, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = portstr ? + (size_t)(portstr - connect_to) : strlen(connect_to); + } + + if(!pp.host_user.len) { /* no hostname found, only port switch */ + pp.host_user.str = dest->user_hostname; + pp.host_user.len = strlen(dest->user_hostname); + } + + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + + if(portstr && portstr[1]) { + const char *p = portstr + 1; + curl_off_t portparse; + if(curlx_str_number(&p, &portparse, 0xffff)) { + failf(data, "No valid port number in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + pp.port = (uint16_t)portparse; /* we know it will fit */ + } + else + pp.port = dest->port; + +#ifndef USE_IPV6 + if(pp.ipv6) { + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); + result = CURLE_NOT_BUILT_IN; + goto out; + } +#endif + + result = peer_create(&pp, ppeer); + CURL_TRC_M(data, "connect-to peer_create2 -> %d", (int)result); + +out: + CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, (int)result); + peer_parse_clear(&pp); + return result; +} + +#ifndef CURL_DISABLE_PROXY + +#ifdef USE_UNIX_SOCKETS +#define UNIX_SOCKET_PREFIX "localhost" +#endif + +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, const char *url) +{ + if(!scheme) + return CURLE_OK; + + if(curl_strequal("https", scheme)) { + if(*proxytype != CURLPROXY_HTTPS2 && *proxytype != CURLPROXY_HTTPS3) + *proxytype = CURLPROXY_HTTPS; + } + else if(curl_strequal("socks5h", scheme)) + *proxytype = CURLPROXY_SOCKS5_HOSTNAME; + else if(curl_strequal("socks5", scheme)) + *proxytype = CURLPROXY_SOCKS5; + else if(curl_strequal("socks4a", scheme)) + *proxytype = CURLPROXY_SOCKS4A; + else if(curl_strequal("socks4", scheme) || curl_strequal("socks", scheme)) + *proxytype = CURLPROXY_SOCKS4; + else if(curl_strequal("http", scheme)) { + if(*proxytype != CURLPROXY_HTTP_1_0) + *proxytype = CURLPROXY_HTTP; + } + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", url); + return CURLE_COULDNT_CONNECT; + } + return CURLE_OK; +} + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype) +{ + struct peer_parse pp; + char *scheme = NULL; + char *portptr = NULL; +#ifdef USE_UNIX_SOCKETS + bool is_socks = FALSE; +#endif + CURLUcode uc; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.port = CURL_DEFAULT_PROXY_PORT; + uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, + CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* URL came without scheme, the passed `proxytype` determines it */ + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + pp.scheme = &Curl_scheme_http; + break; + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: + pp.scheme = &Curl_scheme_https; + break; + case CURLPROXY_SOCKS4: + pp.scheme = &Curl_scheme_socks4; + break; + case CURLPROXY_SOCKS4A: + pp.scheme = &Curl_scheme_socks4a; + break; + case CURLPROXY_SOCKS5: + pp.scheme = &Curl_scheme_socks5; + break; + case CURLPROXY_SOCKS5_HOSTNAME: + pp.scheme = &Curl_scheme_socks5h; + break; + default: + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + } + else { + pp.scheme = Curl_get_scheme(scheme); + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, url); + if(result) + goto out; + } + DEBUGASSERT(pp.scheme); + + if(CURL_PROXY_IS_HTTPS(proxytype) && + !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", url); + result = CURLE_NOT_BUILT_IN; + goto out; + } + + switch(pp.scheme->family) { + case CURLPROTO_SOCKS: +#ifdef USE_UNIX_SOCKETS + is_socks = TRUE; +#endif + break; + case CURLPROTO_HTTP: + break; + default: + failf(data, "Unsupported proxy protocol for \'%s\'", url); + result = CURLE_COULDNT_CONNECT; + goto out; + } + + uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(portptr) { + curl_off_t num; + const char *p = portptr; + if(!curlx_str_number(&p, &num, UINT16_MAX)) + pp.port = (uint16_t)num; + /* Should we not error out when the port number is invalid? */ + curlx_free(portptr); + } + else { + /* No port in URL, take the set one or the scheme's default */ + if(data->set.proxyport) + pp.port = data->set.proxyport; + else + pp.port = pp.scheme->defport; + } + + /* now, clone the proxy hostname */ + uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pp.host_user.str = pp.tmp_host_user; + pp.host_user.len = strlen(pp.tmp_host_user); + +#ifdef USE_UNIX_SOCKETS + if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) { + uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* path will be "/", if no path was found */ + if(strcmp("/", pp.tmp_host)) { + pp.host.str = pp.tmp_host; + pp.host.len = strlen(pp.tmp_host); + pp.unix_socket = TRUE; + } + else { + pp.host = pp.host_user; + } + } +#endif /* USE_UNIX_SOCKETS */ + + if(!pp.host.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + + uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(pp.tmp_zoneid) { + pp.zoneid.str = pp.tmp_zoneid; + pp.zoneid.len = strlen(pp.tmp_zoneid); + } + + *pproxytype = proxytype; + result = peer_create(&pp, ppeer); + +out: + peer_parse_clear(&pp); + curlx_free(scheme); +#ifdef DEBUGBUILD + if(!result) + DEBUGASSERT(*ppeer); +#endif + return result; +} + +#endif /* !CURL_DISABLE_PROXY */ diff --git a/deps/curl/lib/peer.h b/deps/curl/lib/peer.h new file mode 100644 index 0000000000..1ceb230739 --- /dev/null +++ b/deps/curl/lib/peer.h @@ -0,0 +1,110 @@ +#ifndef HEADER_CURL_PEER_H +#define HEADER_CURL_PEER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_scheme; +struct urlpieces; + +/* if peer hostname starts with this, the peer is a unix domain socket + * path, e.g. the remainder after 'localhost'. */ +#define CURL_PEER_UDS_PREFIX "localhost/" + +struct Curl_peer { + const struct Curl_scheme *scheme; /* url scheme */ + char *hostname; /* normalized hostname (IDN decoded when supported) */ + char *zoneid; /* NULL or ipv6 zone identifier */ + uint32_t refcount; /* created with 1, freed when dropping to 0 */ + uint32_t scopeid; /* != 0, ipv6 scope to use */ + uint16_t port; + BIT(unix_socket); /* hostname is a UDS path without the prefix */ + BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */ + BIT(ipv6); /* hostname is an IPv6 address stripped of '[]' */ + char user_hostname[1]; /* hostname supplied by user/url */ +}; + +/* Create a new peer: + * - `peer->user_hostname` is the passed `hostname` + * - `peer->hostname` is the normalized `hostname` via + * + IDN conversion if it has non-ASCII characters + * + stripping of surrounding '[]' for URL formatted ipv6 addresses + * + the path alone in case of a unix domain socket, e.g. hostname + * starts with CURL_PEER_UDS_PREFIX and is longer + * Scans for IPv6 addresses even without surrounding '[]'. + * - `zoneid` ipv6 zone identifier or NULL + * - `scopeid` ipv6 scopeid of zoneid, when known. + */ +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer); + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer); +#endif + +/* Unlink any peer in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src); + +/* Drop a reference, peer may be passed as NULL */ +void Curl_peer_unlink(struct Curl_peer **ppeer); + +/* TRUE if both peers are NULL or have completely same properties. */ +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2); + +/* TRUE if both peers are NULL or have same properties except the scheme. */ +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer); + +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer); + +#ifndef CURL_DISABLE_PROXY + +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, + const char *url); + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype); +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_PEER_H */ diff --git a/deps/curl/lib/pingpong.c b/deps/curl/lib/pingpong.c index 44e424418d..b40d968b3f 100644 --- a/deps/curl/lib/pingpong.c +++ b/deps/curl/lib/pingpong.c @@ -120,13 +120,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow) { - DEBUGASSERT(!pp->initialised); + DEBUGASSERT(!pp->initialized); pp->nread_resp = 0; pp->response = *pnow; /* start response time-out */ pp->pending_resp = TRUE; curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD); curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD); - pp->initialised = TRUE; + pp->initialized = TRUE; } /*********************************************************************** @@ -152,7 +152,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); - DEBUGASSERT(pp->sendthis == NULL); + DEBUGASSERT(!pp->sendthis); if(!conn) /* cannot send without a connection! */ @@ -292,6 +292,13 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, the line is not really terminated until the LF comes */ size_t length = nl - line + 1; + if(memchr(line, 0, length)) { + /* The response line is passed on as a "header" below, so reject an + embedded nul the same way verify_header() does for HTTP. */ + failf(data, "Nul byte in server response line"); + return CURLE_WEIRD_SERVER_REPLY; + } + /* output debug output if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, line, length); @@ -389,7 +396,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, CURLcode Curl_pp_disconnect(struct pingpong *pp) { - if(pp->initialised) { + if(pp->initialized) { curlx_dyn_free(&pp->sendbuf); curlx_dyn_free(&pp->recvbuf); memset(pp, 0, sizeof(*pp)); diff --git a/deps/curl/lib/pingpong.h b/deps/curl/lib/pingpong.h index 864f2c6835..02f4961230 100644 --- a/deps/curl/lib/pingpong.h +++ b/deps/curl/lib/pingpong.h @@ -64,7 +64,7 @@ struct pingpong { CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn); bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn, const char *ptr, size_t len, int *code); - BIT(initialised); + BIT(initialized); BIT(pending_resp); /* set TRUE when a server response is pending or in progress, and is cleared once the last response is read */ diff --git a/deps/curl/lib/pop3.c b/deps/curl/lib/pop3.c index 317c04bbe3..d609b4c2b3 100644 --- a/deps/curl/lib/pop3.c +++ b/deps/curl/lib/pop3.c @@ -231,7 +231,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) */ static CURLcode pop3_parse_url_path(struct Curl_easy *data) { - /* The POP3 struct is already initialised in pop3_connect() */ + /* The POP3 struct is already initialized in pop3_connect() */ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); const char *path = &data->state.up.path[1]; /* skip leading path */ @@ -485,7 +485,8 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, return CURLE_FAILED_INIT; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ @@ -495,7 +496,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!pop3c->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { pop3c->ssldone = ssldone; /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ @@ -527,7 +528,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!conn->creds) { pop3_state(data, POP3_STOP); return result; @@ -535,7 +536,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Send the USER command */ result = Curl_pp_sendf(data, &pop3c->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) pop3_state(data, POP3_USER); @@ -564,7 +565,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -578,17 +579,18 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp, curlx_uztoui(strlen(pop3c->apoptimestamp))); - Curl_MD5_update(ctxt, (const unsigned char *)conn->passwd, - curlx_uztoui(strlen(conn->passwd))); + Curl_MD5_update(ctxt, (const unsigned char *)Curl_creds_passwd(conn->creds), + curlx_uztoui(strlen(Curl_creds_passwd(conn->creds)))); /* Finalise the digest */ Curl_MD5_final(ctxt, digest); - /* Convert the calculated 16 octet digest into a 32 byte hex string */ + /* Convert the calculated 16 octet digest into a 32-byte hex string */ for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]); - result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); + result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", + Curl_creds_user(conn->creds), secret); if(!result) pop3_state(data, POP3_APOP); @@ -1038,7 +1040,8 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, } else /* Send the PASS command */ - result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", conn->passwd); + result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", + Curl_creds_passwd(conn->creds)); if(!result) pop3_state(data, POP3_PASS); @@ -1437,7 +1440,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) pop3c->preftype = POP3_TYPE_ANY; Curl_sasl_init(&pop3c->sasl, data, &saslpop3); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(pp, Curl_pgrs_now(data)); /* Parse the URL options */ diff --git a/deps/curl/lib/progress.c b/deps/curl/lib/progress.c index 919c151e75..969b29a275 100644 --- a/deps/curl/lib/progress.c +++ b/deps/curl/lib/progress.c @@ -240,6 +240,30 @@ void Curl_pgrsSendPause(struct Curl_easy *data, bool enable) } } +#ifdef CURLVERBOSE +static const char * const pgrs_timer_names[] = { + "PGRS-NONE", + "PGRS-STARTOP", + "PGRS-STARTSINGLE", + "PGRS-POSTQUEUE", + "PGRS-NAMELOOKUP", + "PGRS-CONNECT", + "PGRS-APPCONNECT", + "PGRS-PRETRANSFER", + "PGRS-STARTTRANSFER", + "PGRS-POSTRANSFER", + "PGRS-STARTACCEPT", + "PGRS-REDIRECT", +}; + +static const char *pgrs_timer_name(timerid timer) +{ + if((size_t)timer < CURL_ARRAYSIZE(pgrs_timer_names)) + return pgrs_timer_names[(size_t)timer]; + return "?"; +} +#endif /* CURLVERBOSE */ + /* * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ @@ -285,7 +309,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, delta = &data->progress.t_pretransfer; break; case TIMER_STARTTRANSFER: - delta = &data->progress.t_starttransfer; /* prevent updating t_starttransfer unless: * 1. this is the first time we are setting t_starttransfer * 2. a redirect has occurred since the last time t_starttransfer was set @@ -293,12 +316,12 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, * changing the t_starttransfer time. */ if(data->progress.is_t_startransfer_set) { + CURL_TRC_M(data, "[%s] ignored", pgrs_timer_name(timer)); return; } - else { - data->progress.is_t_startransfer_set = TRUE; - break; - } + data->progress.is_t_startransfer_set = TRUE; + delta = &data->progress.t_starttransfer; + break; case TIMER_POSTRANSFER: delta = &data->progress.t_posttransfer; break; @@ -314,7 +337,11 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; + CURL_TRC_M(data, "[%s] added %" FMT_TIMEDIFF_T "ns", + pgrs_timer_name(timer), us); } + else + CURL_TRC_M(data, "[%s] set", pgrs_timer_name(timer)); } /* @@ -703,3 +730,9 @@ void Curl_pgrsUpdate_nometer(struct Curl_easy *data) { (void)progress_calc(data, Curl_pgrs_now(data)); } + +void Curl_pgrsCompleted(struct Curl_easy *data) +{ + struct Progress * const p = &data->progress; + p->timespent = curlx_ptimediff_us(Curl_pgrs_now(data), &p->start); +} diff --git a/deps/curl/lib/progress.h b/deps/curl/lib/progress.h index 7d419ecb8a..1f2a4e71c3 100644 --- a/deps/curl/lib/progress.h +++ b/deps/curl/lib/progress.h @@ -83,4 +83,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); +void Curl_pgrsCompleted(struct Curl_easy *data); + #endif /* HEADER_CURL_PROGRESS_H */ diff --git a/deps/curl/lib/protocol.c b/deps/curl/lib/protocol.c index c8d43251cf..8d57c058c6 100644 --- a/deps/curl/lib/protocol.c +++ b/deps/curl/lib/protocol.c @@ -153,7 +153,8 @@ const struct Curl_scheme Curl_scheme_https = { CURLPROTO_HTTPS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE | + PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS, /* defport */ }; @@ -361,6 +362,51 @@ const struct Curl_scheme Curl_scheme_smtps = { PORT_SMTPS, /* defport */ }; +const struct Curl_scheme Curl_scheme_socks = { + "socks", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4 = { + "socks4", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4a = { + "socks4a", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5 = { + "socks5", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5h = { + "socks5h", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + const struct Curl_scheme Curl_scheme_telnet = { "telnet", /* scheme */ #ifdef CURL_DISABLE_TELNET @@ -397,7 +443,7 @@ const struct Curl_scheme Curl_scheme_ws = { CURLPROTO_WS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTP /* defport */ }; @@ -412,7 +458,7 @@ const struct Curl_scheme Curl_scheme_wss = { CURLPROTO_WSS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS /* defport */ }; @@ -430,49 +476,54 @@ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) 6. make sure this function uses the same hash function that worked for schemetable.c */ - static const struct Curl_scheme * const all_schemes[47] = { - &Curl_scheme_mqtt, - &Curl_scheme_smtp, - &Curl_scheme_tftp, - &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &Curl_scheme_ldaps, - &Curl_scheme_dict, NULL, - &Curl_scheme_file, NULL, - &Curl_scheme_pop3s, - &Curl_scheme_ftp, + static const struct Curl_scheme * const all_schemes[59] = { NULL, + &Curl_scheme_pop3, NULL, + &Curl_scheme_smtps, + &Curl_scheme_socks, + &Curl_scheme_socks4, + &Curl_scheme_socks5, NULL, NULL, + &Curl_scheme_gophers, + &Curl_scheme_ws, + &Curl_scheme_sftp, + &Curl_scheme_socks4a, &Curl_scheme_scp, - &Curl_scheme_mqtts, - &Curl_scheme_imaps, + &Curl_scheme_rtsp, + &Curl_scheme_dict, NULL, NULL, + &Curl_scheme_gopher, NULL, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_smb, NULL, &Curl_scheme_ldap, - &Curl_scheme_http, - &Curl_scheme_smb, NULL, NULL, - &Curl_scheme_telnet, + &Curl_scheme_ldaps, + &Curl_scheme_imap, NULL, NULL, NULL, + &Curl_scheme_imaps, &Curl_scheme_https, - &Curl_scheme_gopher, - &Curl_scheme_rtsp, NULL, NULL, - &Curl_scheme_wss, NULL, - &Curl_scheme_gophers, - &Curl_scheme_smtps, - &Curl_scheme_pop3, - &Curl_scheme_ws, NULL, NULL, - &Curl_scheme_sftp, - &Curl_scheme_ftps, NULL, - &Curl_scheme_smbs, NULL, + &Curl_scheme_tftp, + &Curl_scheme_telnet, NULL, NULL, NULL, + &Curl_scheme_file, + &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ftp, + &Curl_scheme_mqtt, NULL, + &Curl_scheme_socks5h, + &Curl_scheme_http, + &Curl_scheme_pop3s, NULL, + &Curl_scheme_mqtts, NULL, + &Curl_scheme_smbs, + &Curl_scheme_ftps, }; if(len && (len <= 7)) { const char *s = scheme; size_t l = len; const struct Curl_scheme *h; - unsigned int c = 792; + unsigned int c = 443; while(l) { - c <<= 4; + c <<= 5; c += (unsigned int)Curl_raw_tolower(*s); s++; l--; } - h = all_schemes[c % 47]; + h = all_schemes[c % 59]; if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) return h; } diff --git a/deps/curl/lib/protocol.h b/deps/curl/lib/protocol.h index f8254096e2..50e320d0f6 100644 --- a/deps/curl/lib/protocol.h +++ b/deps/curl/lib/protocol.h @@ -51,6 +51,7 @@ struct easy_pollset; #define PORT_SMTPS 465 /* sometimes called SSMTP */ #define PORT_RTSP 554 #define PORT_GOPHER 70 +#define PORT_SOCKS 1080 #define PORT_MQTT 1883 #define PORT_MQTTS 8883 @@ -62,6 +63,7 @@ struct easy_pollset; #define CURLPROTO_WS (1L << 30) #define CURLPROTO_WSS ((curl_prot_t)1 << 31) #define CURLPROTO_MQTTS (1LL << 32) +#define CURLPROTO_SOCKS (1LL << 33) #define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) @@ -103,6 +105,12 @@ typedef enum { FOLLOW_REDIR /* a full true redirect */ } followtype; +typedef enum { + DOMORE_GOBACK = -1, + DOMORE_INCOMPLETE = 0, + DOMORE_DONE = 1 +} domore; + /* * Specific protocol handler, an implementation of one or more URI schemes. */ @@ -118,9 +126,13 @@ struct Curl_protocol { /* If the curl_do() function is better made in two halves, this * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. + * for doing the FTP stuff after the PASV/PORT command. The second + * argument is an output parameter that MUST be set to one of the + * DOMORE_* values: DOMORE_INCOMPLETE if more do_more work remains, + * DOMORE_DONE when the second phase is complete, or DOMORE_GOBACK + * to return to the regular DO/DOING handling. */ - CURLcode (*do_more)(struct Curl_easy *, int *); + CURLcode (*do_more)(struct Curl_easy *, domore *); /* This function *MAY* be set to a protocol-dependent function that is run * after the connect() and everything is done, as a step in the connection. @@ -224,6 +236,9 @@ struct Curl_protocol { SSL connection in the same family without having PROTOPT_SSL. */ #define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ +#define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */ +#define PROTOPT_HTTP_PROXY_TUNNEL (1 << 18) /* Using this protocol with a + * HTTP proxy requires tunneling */ /* Everything about a URI scheme. */ struct Curl_scheme { @@ -268,6 +283,11 @@ extern const struct Curl_scheme Curl_scheme_smb; extern const struct Curl_scheme Curl_scheme_smbs; extern const struct Curl_scheme Curl_scheme_smtp; extern const struct Curl_scheme Curl_scheme_smtps; +extern const struct Curl_scheme Curl_scheme_socks; +extern const struct Curl_scheme Curl_scheme_socks4; +extern const struct Curl_scheme Curl_scheme_socks4a; +extern const struct Curl_scheme Curl_scheme_socks5; +extern const struct Curl_scheme Curl_scheme_socks5h; extern const struct Curl_scheme Curl_scheme_telnet; extern const struct Curl_scheme Curl_scheme_tftp; extern const struct Curl_scheme Curl_scheme_ws; diff --git a/deps/curl/lib/proxy.c b/deps/curl/lib/proxy.c new file mode 100644 index 0000000000..920dc45598 --- /dev/null +++ b/deps/curl/lib/proxy.c @@ -0,0 +1,673 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_PROXY + +#include "urldata.h" +#include "curl_trc.h" +#include "protocol.h" +#include "proxy.h" +#include "http_proxy.h" +#include "strcase.h" +#include "url.h" +#include "vauth/vauth.h" +#include "curlx/inet_pton.h" +#include "curlx/strparse.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +/* + * cidr4_match() returns TRUE if the given IPv4 address is within the + * specified CIDR address range. + * + * @unittest 1614 + */ +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits) +{ + unsigned int address = 0; + unsigned int check = 0; + + if(bits > 32) + /* strange input */ + return FALSE; + + if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) + return FALSE; + if(curlx_inet_pton(AF_INET, network, &check) != 1) + return FALSE; + + if(bits && (bits != 32)) { + unsigned int mask = 0xffffffff << (32 - bits); + unsigned int haddr = htonl(address); + unsigned int hcheck = htonl(check); +#if 0 + curl_mfprintf(stderr, "Host %s (%x) network %s (%x) " + "bits %u mask %x => %x\n", + ipv4, haddr, network, hcheck, bits, mask, + (haddr ^ hcheck) & mask); +#endif + if((haddr ^ hcheck) & mask) + return FALSE; + return TRUE; + } + return address == check; +} + +/* @unittest 1614 */ +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits); +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits) +{ +#ifdef USE_IPV6 + unsigned int bytes; + unsigned int rest; + unsigned char address[16]; + unsigned char check[16]; + + if(!bits) + bits = 128; + + bytes = bits / 8; + rest = bits & 0x07; + if((bytes > 16) || ((bytes == 16) && rest)) + return FALSE; + if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) + return FALSE; + if(curlx_inet_pton(AF_INET6, network, check) != 1) + return FALSE; + if(bytes && memcmp(address, check, bytes)) + return FALSE; + if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) + return FALSE; + + return TRUE; +#else + (void)ipv6; + (void)network; + (void)bits; + return FALSE; +#endif +} + +enum nametype { + TYPE_HOST, + TYPE_IPV4, + TYPE_IPV6 +}; + +static bool match_host(const char *token, size_t tokenlen, + const char *name, size_t namelen) +{ + bool match = FALSE; + + /* ignore trailing dots in the token to check */ + if(token[tokenlen - 1] == '.') + tokenlen--; + + if(tokenlen && (*token == '.')) { + /* ignore leading token dot as well */ + token++; + tokenlen--; + } + /* A: example.com matches 'example.com' + B: www.example.com matches 'example.com' + C: nonexample.com DOES NOT match 'example.com' + */ + if(tokenlen == namelen) + /* case A, exact match */ + match = curl_strnequal(token, name, namelen); + else if(tokenlen < namelen) { + /* case B, tailmatch domain */ + match = (name[namelen - tokenlen - 1] == '.') && + curl_strnequal(token, name + (namelen - tokenlen), tokenlen); + } + /* case C passes through, not a match */ + return match; +} + +static bool match_ip(int type, const char *token, size_t tokenlen, + const char *name) +{ + char *slash; + unsigned int bits = 0; + char checkip[128]; + if(tokenlen >= sizeof(checkip)) + /* this cannot match */ + return FALSE; + /* copy the check name to a temp buffer */ + memcpy(checkip, token, tokenlen); + checkip[tokenlen] = 0; + + slash = strchr(checkip, '/'); + /* if the slash is part of this token, use it */ + if(slash) { + curl_off_t value; + const char *p = &slash[1]; + if(curlx_str_number(&p, &value, 128) || *p) + return FALSE; + /* a too large value is rejected in the cidr function below */ + bits = (unsigned int)value; + *slash = 0; /* null-terminate there */ + } + if(type == TYPE_IPV6) + return cidr6_match(name, checkip, bits); + else + return cidr4_match(name, checkip, bits); +} + +/**************************************************************** + * Checks if the host is in the noproxy list. returns TRUE if it matches and + * therefore the proxy should NOT be used. + ****************************************************************/ +/* @unittest 1614 */ +UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy); +UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy) +{ + /* + * If we do not have a hostname at all, like for example with a FILE + * transfer, we have nothing to interrogate the noproxy list with. + */ + if(!name || name[0] == '\0') + return FALSE; + + /* no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + */ + if(no_proxy && no_proxy[0]) { + const char *p = no_proxy; + size_t namelen; + char address[16]; + enum nametype type = TYPE_HOST; + if(!strcmp("*", no_proxy)) + return TRUE; + + /* NO_PROXY was specified and it was not only an asterisk */ + + /* Check if name is an IP address; if not, assume it being a hostname. */ + namelen = strlen(name); + if(curlx_inet_pton(AF_INET, name, &address) == 1) + type = TYPE_IPV4; +#ifdef USE_IPV6 + else if(curlx_inet_pton(AF_INET6, name, &address) == 1) + type = TYPE_IPV6; +#endif + else { + /* ignore trailing dots in the hostname */ + if(name[namelen - 1] == '.') + namelen--; + } + + while(*p) { + const char *token; + size_t tokenlen = 0; + + /* pass blanks */ + curlx_str_passblanks(&p); + + token = p; + /* pass over the pattern */ + while(*p && !ISBLANK(*p) && (*p != ',')) { + p++; + tokenlen++; + } + + if(tokenlen) { + bool match = FALSE; + if(type == TYPE_HOST) + match = match_host(token, tokenlen, name, namelen); + else + match = match_ip(type, token, tokenlen, name); + + if(match) + return TRUE; + } + + /* pass blanks after pattern */ + curlx_str_passblanks(&p); + /* if not a comma, this ends the loop */ + if(*p != ',') + break; + /* pass any number of commas */ + while(*p == ',') + p++; + } /* while(*p) */ + } /* NO_PROXY was specified and it was not only an asterisk */ + + return FALSE; +} + +#ifndef CURL_DISABLE_HTTP + +/**************************************************************** + * Detect what (if any) proxy to use. Remember that this selects a host + * name and is not limited to HTTP proxies only. + * The returned pointer must be freed by the caller. + ****************************************************************/ +static char *proxy_detect_proxy(struct Curl_easy *data, + const struct Curl_scheme *scheme) +{ + char *proxy = NULL; + + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions do not exist. + */ + char proxy_env[20]; + const char *envp; + VERBOSE(envp = proxy_env); + + curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name); + + /* read the protocol proxy: */ + proxy = curl_getenv(proxy_env); + + /* + * We do not try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!proxy && !curl_strequal("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); + proxy = curl_getenv(proxy_env); + } + + if(!proxy) { +#ifndef CURL_DISABLE_WEBSOCKETS + /* websocket proxy fallbacks */ + if(curl_strequal("ws_proxy", proxy_env)) { + proxy = curl_getenv("http_proxy"); + } + else if(curl_strequal("wss_proxy", proxy_env)) { + proxy = curl_getenv("https_proxy"); + if(!proxy) + proxy = curl_getenv("HTTPS_PROXY"); + } + if(!proxy) { +#endif + envp = "all_proxy"; + proxy = curl_getenv(envp); /* default proxy to use */ + if(!proxy) { + envp = "ALL_PROXY"; + proxy = curl_getenv(envp); + } +#ifndef CURL_DISABLE_WEBSOCKETS + } +#endif + } + if(proxy) + infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); + + return proxy; +} +#endif /* CURL_DISABLE_HTTP */ + +/* + * If this is supposed to use a proxy, we need to figure out the proxy + * hostname, so that we can reuse an existing connection + * that may exist registered to the same proxy host. + */ +static CURLcode parse_proxy(struct Curl_easy *data, + const char *proxy, + bool for_pre_proxy, + struct proxy_info *proxyinfo) +{ + char *proxyuser = NULL; + char *proxypasswd = NULL; + char *scheme = NULL; + CURLcode result = CURLE_OK; + /* Set the start proxy type for URL scheme guessing */ + uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; + CURLU *uhp = curl_url(); + CURLUcode uc; + + if(!uhp) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* When parsing the proxy, allowing non-supported schemes since we have + these made up ones for proxies. Guess scheme for URLs without it. */ + uc = curl_url_set(uhp, CURLUPART_URL, proxy, + CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); + if(!uc) { + /* parsed okay as a URL - only update proxytype when scheme was explicit */ + uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME); + if(!uc) { + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy); + if(result) + goto error; + } + else if(uc != CURLUE_NO_SCHEME) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* else: no explicit scheme, keep the configured proxytype */ + } + else { + failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, + curl_url_strerror(uc)); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + + result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, + &proxyinfo->peer, &proxytype); + if(result) + goto error; + + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: + if(for_pre_proxy) { + failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + break; + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + break; + default: + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + + /* Is there a username and password given in this proxy URL? */ + uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_USER)) { + result = Curl_uc_to_curlcode(uc); + goto error; + } + uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_PASSWORD)) { + result = Curl_uc_to_curlcode(uc); + goto error; + } + + if(proxyuser || proxypasswd) { + result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], + CREDS_URL, &proxyinfo->creds); + if(result) + goto error; + } + else if(!for_pre_proxy && + (data->set.str[STRING_PROXYUSERNAME] || + data->set.str[STRING_PROXYPASSWORD] || + data->set.str[STRING_PROXY_SERVICE_NAME])) { + /* No user/passwd in URL, if this is not a pre-proxy, the + * CURLOPT_PROXY* settings apply. */ + result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], + data->set.str[STRING_PROXYPASSWORD], + NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], + CREDS_OPTION, &proxyinfo->creds); + } + else + Curl_creds_unlink(&proxyinfo->creds); + + proxyinfo->proxytype = proxytype; + +error: + curlx_free(scheme); + curlx_free(proxyuser); + curlx_free(proxypasswd); + curl_url_cleanup(uhp); +#ifdef DEBUGBUILD + if(!result) { + DEBUGASSERT(proxyinfo); + DEBUGASSERT(proxyinfo->peer); + } +#endif + return result; +} + +/* Is transfer's origin exempted from proxy use? */ +static bool proxy_do_not_proxy(struct Curl_easy *data) +{ + const char *no_proxy; + char *env_no_proxy = NULL; + bool do_not_proxy; + + /* no proxying if the transfer does not use the network */ + if(data->state.origin->scheme->flags & PROTOPT_NONETWORK) + return TRUE; + + no_proxy = data->set.str[STRING_NOPROXY]; + if(!no_proxy) { + const char *p = "no_proxy"; + env_no_proxy = curl_getenv(p); + if(!env_no_proxy) { + p = "NO_PROXY"; + env_no_proxy = curl_getenv(p); + } + if(env_no_proxy) + infof(data, "Uses proxy env variable %s == '%s'", p, env_no_proxy); + no_proxy = env_no_proxy; + } + + do_not_proxy = proxy_check_noproxy(data->state.origin->hostname, no_proxy); + curlx_safefree(env_no_proxy); + return do_not_proxy; +} + +CURLcode Curl_proxy_init_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + char *proxy = NULL; + char *pre_proxy = NULL; + bool do_env_detect = TRUE; + CURLcode result = CURLE_OK; + + /* Enforce no proxy use unless we decide to use one */ + conn->bits.origin_is_proxy = FALSE; + DEBUGASSERT(!conn->socks_proxy.peer); + DEBUGASSERT(!conn->http_proxy.peer); + + if(proxy_do_not_proxy(data)) + goto out; + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + /* the empty config strings disable proxy use and env detects */ + if(data->set.str[STRING_PROXY]) { + if(*data->set.str[STRING_PROXY]) { + proxy = curlx_strdup(data->set.str[STRING_PROXY]); + /* if global proxy is set, this is it */ + if(!proxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else + do_env_detect = FALSE; + } + + if(data->set.str[STRING_PRE_PROXY]) { + if(*data->set.str[STRING_PRE_PROXY]) { + pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); + /* if global socks proxy is set, this is it */ + if(!pre_proxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else + do_env_detect = FALSE; + } + +#ifndef CURL_DISABLE_HTTP + /* None configured, detect possible proxy from environment. */ + if(!proxy && !pre_proxy && do_env_detect) + proxy = proxy_detect_proxy(data, conn->scheme); +#else + (void)do_env_detect; +#endif /* CURL_DISABLE_HTTP */ + + if(!proxy && !pre_proxy) + goto out; + + if(pre_proxy) { + result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); + if(result) + goto out; + } + + if(proxy) { + result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); + if(result) + goto out; + + switch(conn->http_proxy.proxytype) { + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + /* Whoops, it is not an HTTP proxy */ + if(pre_proxy) { + /* and we already have a SOCKS pre-proxy. Cannot have both */ + failf(data, "Having a SOCKS pre-proxy and proxy is not " + "supported with \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + /* switch */ + conn->socks_proxy = conn->http_proxy; + memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); + break; + default: + /* all other types are HTTP */ + break; + } + } + + if(conn->socks_proxy.peer) { + DEBUGASSERT(!CURL_PROXY_IS_ANY_HTTP(conn->socks_proxy.proxytype)); + } + +#ifdef CURL_DISABLE_HTTP + if(conn->http_proxy.peer) { + /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + +#else /* CURL_DISABLE_HTTP */ + if(conn->http_proxy.peer) { + const struct Curl_scheme *scheme = data->state.origin->scheme; + bool tunnel_proxy = (bool)data->set.tunnel_thru_httpproxy; + DEBUGASSERT(CURL_PROXY_IS_ANY_HTTP(conn->http_proxy.proxytype)); + + if(!tunnel_proxy) { + /* Decide if we tunnel through proxy automatically */ + if(conn->via_peer) { + /* With connect-to, we always tunnel */ + tunnel_proxy = TRUE; + } + else if(scheme->flags & PROTOPT_SSL) { + /* If the transfer is supposed to be secure, we tunnel */ + tunnel_proxy = TRUE; + } + else if(scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) { + /* transfer scheme required tunneling */ + tunnel_proxy = TRUE; + } + else if(!(scheme->protocol & PROTO_FAMILY_HTTP) && + !(scheme->flags & PROTOPT_PROXY_AS_HTTP)) { + /* Cannot delegate transfer URL to HTTP proxy */ + tunnel_proxy = TRUE; + } + } + + if(!tunnel_proxy) { + /* HTTP proxy used in forwarding mode. This means the connection + * is really to the proxy and NOT the origin of the transfer. */ + DEBUGASSERT(!conn->via_peer); + Curl_peer_link(&conn->origin, conn->http_proxy.peer); + conn->scheme = conn->http_proxy.peer->scheme; + conn->bits.origin_is_proxy = TRUE; + } + +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!Curl_safecmp(data->state.envproxy, proxy)) { + /* proxy changed */ + Curl_auth_digest_cleanup(&data->state.proxydigest); + curlx_free(data->state.envproxy); + data->state.envproxy = curlx_strdup(proxy); + } +#endif + } +#endif /* !CURL_DISABLE_HTTP */ + +out: + curlx_free(pre_proxy); + curlx_free(proxy); + return result; +} + +#endif /* CURL_DISABLE_PROXY */ diff --git a/deps/curl/lib/proxy.h b/deps/curl/lib/proxy.h new file mode 100644 index 0000000000..307ee003e6 --- /dev/null +++ b/deps/curl/lib/proxy.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_PROXY_H +#define HEADER_CURL_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_PROXY + +struct Curl_easy; +struct Curl_peer; +struct Curl_creds; +struct connectdata; + +struct proxy_info { + struct Curl_peer *peer; /* proxy to this peer */ + struct Curl_creds *creds; /* use these credentials, maybe NULL */ + uint8_t proxytype; /* what kind of proxy that is in use */ +}; + +#define CURL_PROXY_IS_HTTPS(t) \ + (((t) == CURLPROXY_HTTPS) || \ + ((t) == CURLPROXY_HTTPS2) || \ + ((t) == CURLPROXY_HTTPS3)) + +#define CURL_PROXY_IS_HTTP(t) \ + (((t) == CURLPROXY_HTTP) || \ + ((t) == CURLPROXY_HTTP_1_0)) + +#define CURL_PROXY_IS_ANY_HTTP(t) \ + (CURL_PROXY_IS_HTTP(t) || \ + CURL_PROXY_IS_HTTPS(t)) + +CURLcode Curl_proxy_init_conn(struct Curl_easy *data, + struct connectdata *conn); + +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_PROXY_H */ diff --git a/deps/curl/lib/psl.c b/deps/curl/lib/psl.c index e2488aea22..195841f3a3 100644 --- a/deps/curl/lib/psl.c +++ b/deps/curl/lib/psl.c @@ -29,6 +29,10 @@ #include "progress.h" #include "curl_share.h" +#if !defined(PSL_VERSION_NUMBER) || PSL_VERSION_NUMBER < 0x001000 +#error "libpsl 0.16.0 or greater required" +#endif + void Curl_psl_destroy(struct PslCache *pslcache) { if(pslcache->psl) { @@ -65,17 +69,14 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) bool dynamic = FALSE; time_t expires = TIME_T_MAX; -#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000 psl = psl_latest(NULL); - dynamic = psl != NULL; + dynamic = !!psl; /* Take care of possible time computation overflow. */ expires = (now_sec < TIME_T_MAX - PSL_TTL) ? (now_sec + PSL_TTL) : TIME_T_MAX; /* Only get the built-in PSL if we do not already have the "latest". */ if(!psl && !pslcache->dynamic) -#endif - psl = psl_builtin(); if(psl) { diff --git a/deps/curl/lib/rand.c b/deps/curl/lib/rand.c index dd82750ba6..3260fe3345 100644 --- a/deps/curl/lib/rand.c +++ b/deps/curl/lib/rand.c @@ -186,7 +186,7 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ @@ -214,7 +214,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num) /* * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ static const char alnum[] = diff --git a/deps/curl/lib/rand.h b/deps/curl/lib/rand.h index afccd0aac1..a02717074f 100644 --- a/deps/curl/lib/rand.h +++ b/deps/curl/lib/rand.h @@ -37,14 +37,14 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num); /* * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); diff --git a/deps/curl/lib/ratelimit.c b/deps/curl/lib/ratelimit.c index dc013757e9..97af01c788 100644 --- a/deps/curl/lib/ratelimit.c +++ b/deps/curl/lib/ratelimit.c @@ -141,12 +141,14 @@ static void rlimit_tune_steps(struct Curl_rlimit *r, r->step_us = CURL_US_PER_SEC + ((timediff_t)mstep_inc * 1000); r->rate_per_step += rate_inc; r->tokens = r->rate_per_step; + if(r->burst_per_step) { + curl_off_t burst_inc = ((r->burst_per_step * mstep_inc) / 1000); + if(burst_inc) + r->burst_per_step += burst_inc; + } } } } - - if(r->burst_per_step) - r->burst_per_step = r->rate_per_step; } void Curl_rlimit_init(struct Curl_rlimit *r, diff --git a/deps/curl/lib/request.c b/deps/curl/lib/request.c index c414383dc0..56dd2c4a15 100644 --- a/deps/curl/lib/request.c +++ b/deps/curl/lib/request.c @@ -65,9 +65,9 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->httpversion = 0; req->sendbuf_hds_len = 0; - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif result = Curl_client_start(data); @@ -115,9 +115,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) struct curltime t0 = { 0, 0 }; curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif #ifndef CURL_DISABLE_COOKIES curlx_safefree(req->cookiehost); @@ -175,9 +175,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); @@ -271,7 +271,8 @@ static CURLcode req_set_upload_done(struct Curl_easy *data) data->req.upload_done = TRUE; CURL_REQ_CLEAR_SEND(data); - Curl_pgrsTime(data, TIMER_POSTRANSFER); + if(data->mstate >= MSTATE_DID) + Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); if(data->req.upload_aborted) { @@ -333,7 +334,7 @@ static CURLcode req_flush(struct Curl_easy *data) result = Curl_xfer_send_shutdown(data, &done); if(result && data->req.shutdown_err_ignore) { infof(data, "Shutdown send direction error: %d. Broken server? " - "Proceeding as if everything is ok.", result); + "Proceeding as if everything is ok.", (int)result); result = CURLE_OK; done = TRUE; } @@ -392,7 +393,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, blen = curlx_dyn_len(req); /* if the sendbuf is empty and the request without body and * the length to send fits info a sendbuf chunk, we send it directly. - * If `blen` is larger then `chunk_size`, we can not. Because we + * If `blen` is larger than `chunk_size`, we can not. Because we * might have to retry a blocked send later from sendbuf and that * would result in retry sends with a shrunken length. That is trouble. */ if(Curl_bufq_is_empty(&data->req.sendbuf) && diff --git a/deps/curl/lib/request.h b/deps/curl/lib/request.h index 6948d79be7..e67865a984 100644 --- a/deps/curl/lib/request.h +++ b/deps/curl/lib/request.h @@ -114,9 +114,9 @@ struct SingleRequest { wanted */ uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ - char *userpwd; /* auth header */ + char *hd_auth; /* Authorization header, full HTTP/1.x line */ #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; /* proxy auth header */ + char *hd_proxy_auth; /* Proxy-Authorization header, full HTTP/1.x line */ #endif #ifndef CURL_DISABLE_COOKIES char *cookiehost; diff --git a/deps/curl/lib/rtsp.c b/deps/curl/lib/rtsp.c index b08767f377..8c5cdd5643 100644 --- a/deps/curl/lib/rtsp.c +++ b/deps/curl/lib/rtsp.c @@ -288,8 +288,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) const char *p_stream_uri = NULL; const char *p_transport = NULL; const char *p_uagent = NULL; - const char *p_proxyuserpwd = NULL; - const char *p_userpwd = NULL; + const char *p_hd_proxy_auth = NULL; + const char *p_hd_auth = NULL; *done = TRUE; if(!rtsp) @@ -301,18 +301,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; - /* Setup the first_* fields to allow auth details get sent - to this origin */ - - if(!data->state.first_host) { - data->state.first_host = curlx_strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->scheme->protocol; - } - /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. @@ -448,14 +436,14 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* setup the authentication headers */ result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, - p_stream_uri, FALSE); + p_stream_uri, NULL, FALSE); if(result) goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->req.proxyuserpwd; + p_hd_proxy_auth = data->req.hd_proxy_auth; #endif - p_userpwd = data->req.userpwd; + p_hd_auth = data->req.hd_auth; /* Referrer */ curlx_safefree(data->state.aptr.ref); @@ -526,8 +514,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) "%s" /* range */ "%s" /* referrer */ "%s" /* user-agent */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ + "%s" /* hd_proxy_auth */ + "%s" /* hd_auth */ , p_transport ? p_transport : "", p_accept ? p_accept : "", @@ -535,8 +523,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) p_range ? p_range : "", p_referrer ? p_referrer : "", p_uagent ? p_uagent : "", - p_proxyuserpwd ? p_proxyuserpwd : "", - p_userpwd ? p_userpwd : ""); + p_hd_proxy_auth ? p_hd_proxy_auth : "", + p_hd_auth ? p_hd_auth : ""); if(result) goto out; @@ -670,7 +658,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data, while(blen && buf[0] != '$') { if(!in_body && buf[0] == 'R' && data->set.rtspreq != RTSPREQ_RECEIVE) { - if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + if(!strncmp(buf, "RTSP/", (blen < 5) ? blen : 5)) { /* This could be the next response, no consume and return */ if(*pconsumed) { DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " @@ -893,6 +881,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed); if(result) goto out; + buf += consumed; blen -= consumed; } } @@ -904,7 +893,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, * writer deal with it (it will report EXCESS and fail the transfer). */ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d, " "rtspc->state=%d, req.size=%" FMT_OFF_T ")", - blen, rtspc->in_header, data->req.done, rtspc->state, + blen, rtspc->in_header, data->req.done, (int)rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { result = Curl_client_write(data, CLIENTWRITE_BODY | diff --git a/deps/curl/lib/select.h b/deps/curl/lib/select.h index 87b695463f..dbbace4527 100644 --- a/deps/curl/lib/select.h +++ b/deps/curl/lib/select.h @@ -131,7 +131,7 @@ struct easy_pollset { #define CURL_EASY_POLLSET_MAGIC 0x7a657370 #endif -/* allocate and initialise */ +/* allocate and initialize */ struct easy_pollset *Curl_pollset_create(void); /* Initialize before first use */ diff --git a/deps/curl/lib/sendf.c b/deps/curl/lib/sendf.c index 7c977d3b9e..7559f64f84 100644 --- a/deps/curl/lib/sendf.c +++ b/deps/curl/lib/sendf.c @@ -103,7 +103,7 @@ CURLcode Curl_client_start(struct Curl_easy *data) result = r->crt->cntrl(data, r, CURL_CRCNTRL_REWIND); if(result) { failf(data, "rewind of client reader '%s' failed: %d", - r->crt->name, result); + r->crt->name, (int)result); return result; } r = r->next; @@ -183,7 +183,8 @@ static CURLcode cw_download_write(struct Curl_easy *data, bool is_connect = !!(type & CLIENTWRITE_CONNECT); if(!ctx->started_response && - !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { + !(type & CLIENTWRITE_CONNECT) && + (!(type & CLIENTWRITE_INFO) || data->req.upload_done)) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); ctx->started_response = TRUE; } @@ -193,7 +194,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, return CURLE_OK; result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); return result; } @@ -214,7 +215,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, /* BODY arrives although we want none, bail out */ streamclose(data->conn, "ignoring body"); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " - "did not want a BODY", type, nbytes); + "did not want a BODY", (unsigned int)type, nbytes); data->req.download_done = TRUE; if(data->info.header_size) /* if headers have been received, this is fine */ @@ -258,7 +259,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) { result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); if(result) return result; } @@ -396,7 +397,7 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len); CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d", - type, len, result); + (unsigned int)type, len, (int)result); return result; } @@ -697,7 +698,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, case CURL_READFUNC_PAUSE: if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// now, and it cannot pause since the transfer + actually only file:// now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); result = CURLE_READ_ERROR; @@ -731,7 +732,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d", - blen, ctx->total_len, ctx->read_len, result, + blen, ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } @@ -1054,7 +1055,7 @@ static CURLcode cr_lc_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1139,7 +1140,7 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) result = do_init_reader_stack(data, r); out: CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", - len, result); + len, (int)result); return result; } @@ -1217,7 +1218,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, out: CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *nread, *eos); + blen, (int)result, *nread, *eos); return result; } @@ -1403,7 +1404,7 @@ CURLcode Curl_creader_set_buf(struct Curl_easy *data, cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result); + CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, (int)result); return result; } @@ -1436,7 +1437,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) while(reader) { result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE); - CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, result); + CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, (int)result); if(result) break; reader = reader->next; diff --git a/deps/curl/lib/sendf.h b/deps/curl/lib/sendf.h index 75c6e248ea..787fe7ff69 100644 --- a/deps/curl/lib/sendf.h +++ b/deps/curl/lib/sendf.h @@ -377,7 +377,7 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data); * values will be ignored. * @return CURLE_OK if offset could be set * CURLE_READ_ERROR if not supported by reader or seek/read failed - * of offset larger then total length + * of offset larger than total length * CURLE_PARTIAL_FILE if offset led to 0 total length */ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); diff --git a/deps/curl/lib/setopt.c b/deps/curl/lib/setopt.c index b8a632748c..eb9ff2e396 100644 --- a/deps/curl/lib/setopt.c +++ b/deps/curl/lib/setopt.c @@ -108,7 +108,7 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, if(blob) { struct curl_blob *nblob; - if(!blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) + if(!blob->data || !blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; nblob = (struct curl_blob *) curlx_malloc(sizeof(struct curl_blob) + @@ -240,17 +240,9 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, if(auth != CURLAUTH_NONE) { int bitcheck = 0; bool authbits = FALSE; - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - bool iestyle = !!(auth & CURLAUTH_DIGEST_IE); - if(proxy) - data->state.authproxy.iestyle = iestyle; - else - data->state.authhost.iestyle = iestyle; - if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* drop the legacy bit */ } /* switch off bits we cannot support */ @@ -334,7 +326,7 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, if(option != CURLOPT_SSLVERSION) primary = &data->set.proxy_ssl.primary; #else - if(option) {} + (void)option; /* unused */ #endif version = C_SSLVERSION_VALUE(arg); version_max = (long)C_SSLVERSION_MAX_VALUE(arg); @@ -407,22 +399,6 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) } #endif /* !CURL_DISABLE_RTSP */ -#ifdef USE_SSL -static void set_ssl_options(struct ssl_config_data *ssl, - struct ssl_primary_config *config, - long arg) -{ - config->ssl_options = (unsigned char)(arg & 0xff); - ssl->enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - ssl->no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - ssl->no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - ssl->revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - ssl->native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - ssl->auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - ssl->earlydata = !!(arg & CURLSSLOPT_EARLYDATA); -} -#endif - static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, long arg) { @@ -826,7 +802,7 @@ static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, if((arg > ok) || (arg < 0)) /* reserve other values for future use */ infof(data, "boolean setopt(%d) got unsupported argument %ld," - " treated as %d", option, arg, enabled); + " treated as %d", (int)option, arg, enabled); return CURLE_OK; } @@ -855,9 +831,9 @@ static CURLcode setopt_long_net(struct Curl_easy *data, CURLoption option, s->dns_cache_timeout_ms = -1; break; case CURLOPT_MAXCONNECTS: - result = value_range(&arg, 1, 1, INT_MAX); + result = value_range(&arg, 0, 0, INT_MAX); if(!result) - s->maxconnects = (uint32_t)arg; + s->maxconnects = arg ? (uint32_t)arg : DEFAULT_CONNCACHE_SIZE; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: return setopt_set_timeout_sec(&s->server_response_timeout, arg); @@ -1002,11 +978,11 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, s->use_ssl = (unsigned char)arg; break; case CURLOPT_SSL_OPTIONS: - set_ssl_options(&s->ssl, &s->ssl.primary, arg); + s->ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_OPTIONS: - set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); + s->proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #endif case CURLOPT_SSL_ENABLE_NPN: @@ -1027,23 +1003,35 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, #endif /* !USE_SSL */ } +#ifndef CURL_DISABLE_PROXY +static void changeproxy(struct Curl_easy *data) +{ + Curl_auth_digest_cleanup(&data->state.proxydigest); + memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); +} + static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, long arg) { -#ifndef CURL_DISABLE_PROXY struct UserDefined *s = &data->set; switch(option) { case CURLOPT_PROXYPORT: if((arg < 0) || (arg > UINT16_MAX)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(arg != s->proxyport) + changeproxy(data); s->proxyport = (uint16_t)arg; break; case CURLOPT_PROXYAUTH: return httpauth(data, TRUE, (unsigned long)arg); case CURLOPT_PROXYTYPE: - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_HTTPS3)) return CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef USE_PROXY_HTTP3 + if(arg == CURLPROXY_HTTPS3) + return CURLE_NOT_BUILT_IN; +#endif s->proxytype = (unsigned char)arg; break; case CURLOPT_SOCKS5_AUTH: @@ -1055,13 +1043,17 @@ static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, return CURLE_UNKNOWN_OPTION; } return CURLE_OK; +} #else +static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, + long arg) +{ (void)data; (void)option; (void)arg; return CURLE_UNKNOWN_OPTION; -#endif } +#endif static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, long arg) @@ -1276,15 +1268,23 @@ static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, return CURLE_OUT_OF_MEMORY; } } - else + else if(!data->share || !data->share->hsts) { + /* throw away the HSTS cache unless shared */ Curl_hsts_cleanup(&data->hsts); + /* flush all the entries */ + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + } + else + /* detach from shared HSTS cache without freeing it */ + data->hsts = NULL; break; #endif #ifndef CURL_DISABLE_ALTSVC case CURLOPT_ALTSVC_CTRL: return Curl_altsvc_ctrl(data, arg); #endif -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_GSSAPI_DELEGATION: s->gssapi_delegation = (unsigned char)arg & (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); @@ -1515,13 +1515,10 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, #ifdef USE_HTTP2 case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: { - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) - return Curl_data_priority_add_child(dep, data, - option == CURLOPT_STREAM_DEPENDS_E); + case CURLOPT_STREAM_DEPENDS_E: + /* not doing stream dependencies any longer, but accept options + * for backward compatibility */ break; - } #endif default: @@ -1630,13 +1627,12 @@ static CURLcode setproxy(struct Curl_easy *data, const char *proxy) !strcmp(data->set.str[STRING_PROXY], proxy)) return CURLE_OK; /* same one as before */ - Curl_auth_digest_cleanup(&data->state.proxydigest); - memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); + changeproxy(data); return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); } static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, - const char *ptr) + char *ptr) { CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; @@ -1650,16 +1646,16 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, result = setstropt_userpwd(ptr, &u, &p); /* URL decode the components */ - if(!result && u) { + if(!result) { curlx_safefree(s->str[STRING_PROXYUSERNAME]); - result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); - } - if(!result && p) { curlx_safefree(s->str[STRING_PROXYPASSWORD]); + if(u) + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, REJECT_ZERO); - } curlx_free(u); curlx_free(p); break; @@ -1770,20 +1766,24 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * Set CA info SSL connection for proxy. Specify filename of the * CA certificate */ - s->proxy_ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + s->proxy_ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE_PROXY]; + return result; case CURLOPT_PROXY_CRLFILE: /* * Set CRL file info for SSL connection for proxy. Specify filename of the * CRL to check certificates revocation */ - return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT: /* - * Set Issuer certificate file - * to check certificates issuer + * Set Issuer certificate file to check certificates issuer */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_CAPATH: /* * Set CA path info for SSL connection proxy. Specify directory name of the @@ -1792,8 +1792,9 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - s->proxy_ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + s->proxy_ssl.custom_capath = !!s->str[STRING_SSL_CAPATH_PROXY]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -1813,14 +1814,12 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) { CURLcode result = CURLE_OK; + if(s->postfieldsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; if(!ptr || s->postfieldsize == -1) result = Curl_setstropt(&s->str[STRING_COPYPOSTFIELDS], ptr); else { - size_t pflen; - - if(s->postfieldsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); + size_t pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); if(pflen == SIZE_MAX) return CURLE_OUT_OF_MEMORY; else { @@ -1844,37 +1843,79 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) } #endif -static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, - char *ptr) +#ifdef USE_ECH +static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr) { - CURLcode result; struct UserDefined *s = &data->set; -#ifndef CURL_DISABLE_PROXY - result = setopt_cptr_proxy(data, option, ptr); - if(result != CURLE_UNKNOWN_OPTION) - return result; + CURLcode result = CURLE_OK; + + if(!ptr || !strcmp(ptr, "false")) + s->tls_ech = CURLECH_DISABLE; + else { + size_t plen = strlen(ptr); + if(plen > CURL_MAX_INPUT_LENGTH) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + if(!strcmp(ptr, "grease")) + s->tls_ech = CURLECH_GREASE; + else if(!strcmp(ptr, "true")) + s->tls_ech = CURLECH_ENABLE; + else if(!strcmp(ptr, "hard")) + s->tls_ech = CURLECH_HARD; + else if(plen > 4 && !strncmp(ptr, "ecl:", 4)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); + } + else if(plen > 3 && !strncmp(ptr, "pn:", 3)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); + } + else + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + } + return result; +} +#else +#define setopt_ech(x,y) CURLE_NOT_BUILT_IN #endif - result = CURLE_OK; + +#if defined(USE_SSL) || defined(USE_SSH) +/* One of the options is used for both TLS and SSH */ +static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + result = Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); + break; +#ifdef USE_SSL case CURLOPT_CAINFO: /* * Set CA info for SSL connection. Specify filename of the CA certificate */ - s->ssl.custom_cafile = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + s->ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE]; + return result; case CURLOPT_CAPATH: /* * Set CA path info for SSL connection. Specify directory name of the CA * certificates which have been prepared using openssl c_rehash utility. */ -#ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) { /* This does not work on Windows. */ - s->ssl.custom_capath = TRUE; - return Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + s->ssl.custom_capath = !!s->str[STRING_SSL_CAPATH]; + return result; } -#endif return CURLE_NOT_BUILT_IN; case CURLOPT_CRLFILE: /* @@ -1891,27 +1932,106 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, else return CURLE_NOT_BUILT_IN; case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) /* set preferred list of TLS 1.3 cipher suites */ return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST], ptr); - } else return CURLE_NOT_BUILT_IN; case CURLOPT_RANDOM_FILE: break; case CURLOPT_EGDSOCKET: break; - case CURLOPT_REQUEST_TARGET: - return Curl_setstropt(&s->str[STRING_TARGET], ptr); -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC_FILE: + case CURLOPT_SSL_CTX_DATA: /* - * Use this file instead of the $HOME/.netrc file + * Set an SSL_CTX callback parameter pointer */ - return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { + s->ssl.fsslctxp = ptr; + break; + } + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSLCERT: + /* + * String that holds filename of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT], ptr); + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); + case CURLOPT_SSLKEY: + /* + * String that holds filename of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY], ptr); + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ + if(ptr && ptr[0]) { + result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); + if(!result) { + result = Curl_ssl_set_engine(data, ptr); + } + } + break; + case CURLOPT_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_EC_CURVES: + /* + * Set accepted curves in SSL connection setup. + * Specify colon-delimited list of curve algorithm names. + */ + if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) + return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_SIGNATURE_ALGORITHMS: + /* + * Set accepted signature algorithms. + * Specify colon-delimited list of signature scheme names. + */ + if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) + return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. + */ + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_ECH: + return setopt_ech(data, ptr); +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} #endif #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) +static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, + CURLoption option, char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { case CURLOPT_COPYPOSTFIELDS: return setopt_copypostfields(ptr, s); @@ -1924,7 +2044,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; -#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ #ifndef CURL_DISABLE_HTTP case CURLOPT_TRAILERDATA: @@ -1970,6 +2089,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * String to set in the HTTP Referer: field. */ + Curl_bufref_free(&data->state.referer); result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); break; @@ -2013,311 +2133,238 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #endif /* !CURL_DISABLE_COOKIES */ #endif /* !CURL_DISABLE_HTTP */ + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); - - /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we - were using the already set type and this changes the actual request - keyword */ - case CURLOPT_SERVICE_NAME: +#ifdef USE_SSH +static CURLcode setopt_cptr_ssh(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_SSH_PUBLIC_KEYFILE: /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO + * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); - - case CURLOPT_HEADERDATA: + return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); + case CURLOPT_SSH_PRIVATE_KEYFILE: /* - * Custom pointer to pass the header write callback function + * Use this file instead of the $HOME/.ssh/id_dsa file */ - s->writeheader = ptr; - break; - case CURLOPT_READDATA: + return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); + case CURLOPT_SSH_KEYDATA: /* - * FILE pointer to read the file to be uploaded from. Or possibly used as - * argument to the read callback. + * Custom client data to pass to the SSH keyfunc callback */ - s->in_set = ptr; + s->ssh_keyfunc_userp = ptr; break; - case CURLOPT_WRITEDATA: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* - * FILE pointer to write to. Or possibly used as argument to the write - * callback. + * Option to allow for the MD5 of the host public key to be checked + * for validation purposes. */ - s->out = ptr; - break; - case CURLOPT_DEBUGDATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. */ - s->debugdata = ptr; - break; - case CURLOPT_PROGRESSDATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); + case CURLOPT_SSH_KNOWNHOSTS: /* - * Custom client data to pass to the progress callback + * Store the filename to read known hosts from. */ - s->progress_client = ptr; - break; - case CURLOPT_SEEKDATA: + return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); +#ifdef USE_LIBSSH2 + case CURLOPT_SSH_HOSTKEYDATA: /* - * Seek control callback. Might be NULL. + * Custom client data to pass to the SSH keyfunc callback */ - s->seek_client = ptr; + s->ssh_hostkeyfunc_userp = ptr; break; - case CURLOPT_IOCTLDATA: +#endif /* USE_LIBSSH2 */ + default: + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +} +#endif /* USE_SSH */ + +#ifndef CURL_DISABLE_FTP +static CURLcode setopt_cptr_ftp(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_FTPPORT: /* - * I/O control data pointer. Might be NULL. + * Use FTP PORT, this also specifies which IP address to use */ - s->ioctl_client = ptr; + result = Curl_setstropt(&s->str[STRING_FTPPORT], ptr); + s->ftp_use_port = !!(s->str[STRING_FTPPORT]); break; - case CURLOPT_SSL_CTX_DATA: - /* - * Set an SSL_CTX callback parameter pointer - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { - s->ssl.fsslctxp = ptr; - break; + + case CURLOPT_FTP_ACCOUNT: + return Curl_setstropt(&s->str[STRING_FTP_ACCOUNT], ptr); + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); + + case CURLOPT_KRBLEVEL: + return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ + case CURLOPT_CHUNK_DATA: + s->wildcardptr = ptr; + break; + case CURLOPT_FNMATCH_DATA: + s->fnmatch_data = ptr; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_FTP */ + +static CURLcode setopt_cptr_net(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_INTERFACE: + /* + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. + */ + return setstropt_interface(ptr, + &s->str[STRING_DEVICE], + &s->str[STRING_INTERFACE], + &s->str[STRING_BINDHOST]); +#ifdef USE_RESOLV_ARES + case CURLOPT_DNS_SERVERS: + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); + + case CURLOPT_DNS_INTERFACE: + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); + + case CURLOPT_DNS_LOCAL_IP4: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); + + case CURLOPT_DNS_LOCAL_IP6: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); +#endif +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + s->abstract_unix_socket = FALSE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); + + case CURLOPT_ABSTRACT_UNIX_SOCKET: + s->abstract_unix_socket = TRUE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); +#endif +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_URL: + { + CURLcode result = Curl_setstropt(&s->str[STRING_DOH], ptr); + s->doh = !!(s->str[STRING_DOH]); + return result; } - else #endif - return CURLE_NOT_BUILT_IN; + default: + return CURLE_UNKNOWN_OPTION; + } +} + +static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_REQUEST_TARGET: + return Curl_setstropt(&s->str[STRING_TARGET], ptr); +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC_FILE: + return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); +#endif + case CURLOPT_CUSTOMREQUEST: + return Curl_setstropt(&s->str[STRING_CUSTOMREQUEST], ptr); + + /* we do not set s->method = HTTPREQ_CUSTOM; here, we continue as if we + were using the already set type and this changes the actual request + keyword */ + case CURLOPT_SERVICE_NAME: + return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); + + case CURLOPT_HEADERDATA: + s->writeheader = ptr; + break; + case CURLOPT_READDATA: + s->in_set = ptr; + break; + case CURLOPT_WRITEDATA: + s->out = ptr; + break; + case CURLOPT_DEBUGDATA: + s->debugdata = ptr; + break; + case CURLOPT_PROGRESSDATA: + s->progress_client = ptr; + break; + case CURLOPT_SEEKDATA: + s->seek_client = ptr; + break; + case CURLOPT_IOCTLDATA: + s->ioctl_client = ptr; + break; case CURLOPT_SOCKOPTDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->sockopt_client = ptr; break; case CURLOPT_OPENSOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->opensocket_client = ptr; break; case CURLOPT_RESOLVER_START_DATA: - /* - * resolver start callback data pointer. Might be NULL. - */ s->resolver_start_client = ptr; break; case CURLOPT_CLOSESOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->closesocket_client = ptr; break; case CURLOPT_PREREQDATA: s->prereq_userp = ptr; break; case CURLOPT_ERRORBUFFER: - /* - * Error buffer provided by the caller to get the human readable error - * string in. - */ s->errorbuffer = ptr; break; - -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTPPORT: - /* - * Use FTP PORT, this also specifies which IP address to use - */ - result = Curl_setstropt(&s->str[STRING_FTPPORT], ptr); - s->ftp_use_port = !!(s->str[STRING_FTPPORT]); - break; - - case CURLOPT_FTP_ACCOUNT: - return Curl_setstropt(&s->str[STRING_FTP_ACCOUNT], ptr); - - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); - - case CURLOPT_KRBLEVEL: - return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ - case CURLOPT_CHUNK_DATA: - s->wildcardptr = ptr; - break; - case CURLOPT_FNMATCH_DATA: - s->fnmatch_data = ptr; - break; -#endif case CURLOPT_URL: - /* - * The URL to fetch. - */ result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); break; case CURLOPT_USERPWD: - /* - * user:password to use in the operation - */ return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], &s->str[STRING_PASSWORD]); case CURLOPT_USERNAME: - /* - * authentication username to use in the operation - */ return Curl_setstropt(&s->str[STRING_USERNAME], ptr); case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ return Curl_setstropt(&s->str[STRING_BEARER], ptr); case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - case CURLOPT_SSLCERT: - /* - * String that holds filename of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT], ptr); - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); - case CURLOPT_SSLKEY: - /* - * String that holds filename of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY], ptr); - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - if(ptr && ptr[0]) { - result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); - if(!result) { - result = Curl_ssl_set_engine(data, ptr); - } - } - break; - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - return setstropt_interface(ptr, - &s->str[STRING_DEVICE], - &s->str[STRING_INTERFACE], - &s->str[STRING_BINDHOST]); - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); - return CURLE_NOT_BUILT_IN; case CURLOPT_PRIVATE: - /* - * Set private data pointer. - */ s->private_data = ptr; break; -#ifdef USE_SSL - case CURLOPT_SSL_EC_CURVES: - /* - * Set accepted curves in SSL connection setup. - * Specify colon-delimited list of curve algorithm names. - */ - if(Curl_ssl_supports(data, SSLSUPP_SSL_EC_CURVES)) - return Curl_setstropt(&s->str[STRING_SSL_EC_CURVES], ptr); - return CURLE_NOT_BUILT_IN; - case CURLOPT_SSL_SIGNATURE_ALGORITHMS: - /* - * Set accepted signature algorithms. - * Specify colon-delimited list of signature scheme names. - */ - if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) - return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], ptr); - return CURLE_NOT_BUILT_IN; - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); - return CURLE_NOT_BUILT_IN; -#endif -#ifdef USE_SSH - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa.pub file - */ - return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); - case CURLOPT_SSH_PRIVATE_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa file - */ - return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_keyfunc_userp = ptr; - break; -#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - /* - * Option to allow for the MD5 of the host public key to be checked - * for validation purposes. - */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the filename to read known hosts from. - */ - return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); -#endif -#ifdef USE_LIBSSH2 - case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: - /* - * Option to allow for the SHA256 of the host public key to be checked - * for validation purposes. - */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); - case CURLOPT_SSH_HOSTKEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_hostkeyfunc_userp = ptr; - break; -#endif /* USE_LIBSSH2 */ -#endif /* USE_SSH */ case CURLOPT_PROTOCOLS_STR: if(ptr) { curl_prot_t protos; @@ -2356,21 +2403,10 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * resuming a previously established RTSP session - */ return Curl_setstropt(&s->str[STRING_RTSP_SESSION_ID], ptr); case CURLOPT_RTSP_STREAM_URI: - /* - * Set the Stream URI for the RTSP request. Unless the request is - * for generic server options, the application will need to set this. - */ return Curl_setstropt(&s->str[STRING_RTSP_STREAM_URI], ptr); case CURLOPT_RTSP_TRANSPORT: - /* - * The content of the Transport: header for the RTSP request - */ return Curl_setstropt(&s->str[STRING_RTSP_TRANSPORT], ptr); case CURLOPT_INTERLEAVEDATA: s->rtp_out = ptr; @@ -2396,37 +2432,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, break; #endif #endif -#ifdef USE_RESOLV_ARES - case CURLOPT_DNS_SERVERS: - return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - - case CURLOPT_DNS_INTERFACE: - return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - - case CURLOPT_DNS_LOCAL_IP4: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - - case CURLOPT_DNS_LOCAL_IP6: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - -#endif -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - s->abstract_unix_socket = FALSE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - - case CURLOPT_ABSTRACT_UNIX_SOCKET: - s->abstract_unix_socket = TRUE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - -#endif - -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_URL: - result = Curl_setstropt(&s->str[STRING_DOH], ptr); - s->doh = !!(s->str[STRING_DOH]); - break; -#endif #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTSREADDATA: s->hsts_read_userp = ptr; @@ -2481,44 +2486,48 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_altsvc_load(data->asi, ptr); break; #endif /* !CURL_DISABLE_ALTSVC */ -#ifdef USE_ECH - case CURLOPT_ECH: { - size_t plen = 0; - - if(!ptr) { - s->tls_ech = CURLECH_DISABLE; - break; - } - plen = strlen(ptr); - if(plen > CURL_MAX_INPUT_LENGTH) { - s->tls_ech = CURLECH_DISABLE; - return CURLE_BAD_FUNCTION_ARGUMENT; - } - /* set tls_ech flag value, preserving CLA_CFG bit */ - if(!strcmp(ptr, "false")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_DISABLE; - else if(!strcmp(ptr, "grease")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_GREASE; - else if(!strcmp(ptr, "true")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_ENABLE; - else if(!strcmp(ptr, "hard")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_HARD; - else if(plen > 5 && !strncmp(ptr, "ecl:", 4)) { - result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); - if(!result) - s->tls_ech |= CURLECH_CLA_CFG; - } - else if(plen > 4 && !strncmp(ptr, "pn:", 3)) - result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); - break; - } -#endif + case CURLOPT_ECH: + return setopt_ech(data, ptr); default: return CURLE_UNKNOWN_OPTION; } return result; } +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + typedef CURLcode (*ptrfunc)(struct Curl_easy *data, CURLoption option, + char *ptr); + static const ptrfunc setopt_call[] = { +#ifndef CURL_DISABLE_PROXY + setopt_cptr_proxy, +#endif +#if defined(USE_SSL) || defined(USE_SSH) + setopt_cptr_ssl, +#endif +#ifdef USE_SSH + setopt_cptr_ssh, +#endif +#ifndef CURL_DISABLE_FTP + setopt_cptr_ftp, +#endif +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) + setopt_cptr_http_mqtt, +#endif + setopt_cptr_net, + setopt_cptr_misc, + }; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(setopt_call); i++) { + CURLcode result = setopt_call[i](data, option, ptr); + if(result != CURLE_UNKNOWN_OPTION) + return result; + } + return CURLE_UNKNOWN_OPTION; +} + static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, va_list param) { @@ -2749,13 +2758,15 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, break; case CURLOPT_MAX_SEND_SPEED_LARGE: /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * When transfer uploads are faster than CURLOPT_MAX_SEND_SPEED_LARGE * bytes per second the transfer is throttled.. */ if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; s->max_send_speed = offt; - Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt, + /* use minimal burst rate of 32k. some protocol batch IO */ + Curl_rlimit_init(&data->progress.ul.rlimit, offt, + CURLMAX(offt, (32 * 1024)), Curl_pgrs_now(data)); break; case CURLOPT_MAX_RECV_SPEED_LARGE: @@ -2766,7 +2777,9 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; s->max_recv_speed = offt; - Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt, + /* use minimal burst rate of 32k. some protocol batch IO */ + Curl_rlimit_init(&data->progress.dl.rlimit, offt, + CURLMAX(offt, (32 * 1024)), Curl_pgrs_now(data)); break; case CURLOPT_RESUME_FROM_LARGE: @@ -2819,15 +2832,20 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) - return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + s->proxy_ssl.custom_cablob = !!s->blobs[BLOB_CAINFO_PROXY]; + return result; + } #endif return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT_BLOB: /* * Blob that holds Issuer certificate to check certificates issuer */ - return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT_BLOB)) + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + return CURLE_NOT_BUILT_IN; #endif case CURLOPT_SSLKEY_BLOB: /* @@ -2841,8 +2859,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - s->ssl.custom_cablob = TRUE; - return Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + s->ssl.custom_cablob = !!s->blobs[BLOB_CAINFO]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -2928,6 +2947,6 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", option); + failf(data, "setopt 0x%x got bad argument", (unsigned int)option); return result; } diff --git a/deps/curl/lib/sha256.c b/deps/curl/lib/sha256.c index d97f45f05f..047119044b 100644 --- a/deps/curl/lib/sha256.c +++ b/deps/curl/lib/sha256.c @@ -43,8 +43,8 @@ * 2. USE_WOLFSSL * 3. USE_GNUTLS * 4. USE_MBEDTLS - * 5. USE_COMMON_CRYPTO - * 6. USE_WIN32_CRYPTO + * 5. USE_WIN32_CRYPTO + * 6. USE_COMMON_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. @@ -113,7 +113,8 @@ static void my_sha256_final(unsigned char *digest, void *in) } #elif defined(USE_GNUTLS) -#include +#include +#include typedef struct sha256_ctx my_sha256_ctx; @@ -132,11 +133,15 @@ static void my_sha256_update(void *ctx, static void my_sha256_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + sha256_digest(ctx, digest); +#else sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); +#endif } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 #include typedef psa_hash_operation_t my_sha256_ctx; diff --git a/deps/curl/lib/smb.c b/deps/curl/lib/smb.c index 6a97d2e006..70af8d5970 100644 --- a/deps/curl/lib/smb.c +++ b/deps/curl/lib/smb.c @@ -61,7 +61,7 @@ enum smb_conn_state { /* SMB connection data, kept at connection */ struct smb_conn { enum smb_conn_state state; - char *user; + const char *user; char *domain; char *share; unsigned char challenge[8]; @@ -467,14 +467,15 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN); - char *slash; + const char *slash; + const char *user = Curl_creds_user(conn->creds); (void)done; if(!smbc) return CURLE_FAILED_INIT; /* Check we have a username and password to authenticate with */ - if(!data->state.aptr.user) + if(!Curl_creds_has_user(data->state.creds)) return CURLE_LOGIN_DENIED; /* Initialize the connection state */ @@ -487,20 +488,20 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* Parse the username, domain, and password */ - slash = strchr(conn->user, '/'); + slash = strchr(user, '/'); if(!slash) - slash = strchr(conn->user, '\\'); + slash = strchr(user, '\\'); if(slash) { smbc->user = slash + 1; - smbc->domain = curlx_strdup(conn->user); + smbc->domain = curlx_strdup(user); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; - smbc->domain[slash - conn->user] = 0; + smbc->domain[slash - user] = 0; } else { - smbc->user = conn->user; - smbc->domain = curlx_strdup(conn->host.name); + smbc->user = user; + smbc->domain = curlx_strdup(conn->origin->hostname); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; } @@ -670,6 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) unsigned char nt_hash[21]; unsigned char nt[24]; size_t byte_count; + const char *passwd = Curl_creds_passwd(conn->creds); if(!smbc || !req) return CURLE_FAILED_INIT; @@ -680,9 +682,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data) if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); + Curl_ntlm_core_mk_lm_hash(passwd, lm_hash); Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); - Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); + Curl_ntlm_core_mk_nt_hash(passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); @@ -720,7 +722,8 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, struct smb_tree_connect msg; struct connectdata *conn = data->conn; char *p = msg.bytes; - const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) + + const size_t byte_count = strlen(conn->origin->hostname) + + strlen(smbc->share) + strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ if(byte_count > sizeof(msg.bytes)) @@ -735,7 +738,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, "\\\\%s\\" /* hostname */ "%s%c" /* share */ "%s", /* service */ - conn->host.name, smbc->share, 0, SERVICENAME); + conn->origin->hostname, smbc->share, 0, SERVICENAME); p++; /* count the final null-termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); @@ -1004,8 +1007,8 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY); struct smb_header *h; enum smb_req_state next_state = SMB_DONE; - unsigned short len; - unsigned short off; + size_t len; + size_t off; CURLcode result; void *msg = NULL; const struct smb_nt_create_response *smb_m; @@ -1228,4 +1231,4 @@ const struct Curl_protocol Curl_protocol_smb = { ZERO_NULL, /* follow */ }; -#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE */ diff --git a/deps/curl/lib/smtp.c b/deps/curl/lib/smtp.c index b5c425cd7c..2283f5e87d 100644 --- a/deps/curl/lib/smtp.c +++ b/deps/curl/lib/smtp.c @@ -182,7 +182,7 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn, static CURLcode smtp_parse_url_path(struct Curl_easy *data, struct smtp_conn *smtpc) { - /* The SMTP struct is already initialised in smtp_connect() */ + /* The SMTP struct is already initialized in smtp_connect() */ const char *path = &data->state.up.path[1]; /* skip leading path */ char localhost[HOSTNAME_MAX + 1]; @@ -347,7 +347,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, /* Get more and convert it when needed */ result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d", - blen, result, nread, eos); + blen, (int)result, nread, eos); if(result) return result; @@ -432,7 +432,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, } *peos = (bool)ctx->eos; DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d", - blen, result, *pnread, *peos)); + blen, (int)result, *pnread, *peos)); return result; } @@ -608,7 +608,7 @@ static void smtp_state(struct Curl_easy *data, * * smtp_perform_ehlo() * - * Sends the EHLO command to not only initialise communication with the ESMTP + * Sends the EHLO command to not only initialize communication with the ESMTP * server but to also obtain a list of server side supported capabilities. */ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, @@ -635,7 +635,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, * * smtp_perform_helo() * - * Sends the HELO command to initialise communication with the SMTP server. + * Sends the HELO command to initialize communication with the SMTP server. */ static CURLcode smtp_perform_helo(struct Curl_easy *data, struct smtp_conn *smtpc) @@ -689,7 +689,8 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS); if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler and SMTP state */ @@ -699,7 +700,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!smtpc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { smtpc->ssldone = ssldone; /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ @@ -1677,7 +1678,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) /* Initialize the SASL storage */ Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data)); /* Parse the URL options */ @@ -1742,7 +1743,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, /* Clear the transfer mode for the next request */ smtp->transfer = PPTRANSFER_BODY; CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d", - status, premature, result); + (int)status, premature, (int)result); return result; } @@ -1803,7 +1804,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, out: CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d", - result, *connected, *dophase_done); + (int)result, *connected, *dophase_done); return result; } @@ -1852,7 +1853,7 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data, result = smtp_dophase_done(data, smtp, connected); CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", - result, *dophase_done); + (int)result, *dophase_done); return result; } @@ -1884,7 +1885,7 @@ static CURLcode smtp_do(struct Curl_easy *data, bool *done) return result; result = smtp_regular_transfer(data, smtpc, smtp, done); - CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); + CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done); return result; } @@ -1935,7 +1936,8 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) DEBUGF(infof(data, "DO phase is complete")); } - CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); + CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result, + *dophase_done); return result; } @@ -1977,7 +1979,7 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; out: - CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); + CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result); return result; } diff --git a/deps/curl/lib/sockaddr.h b/deps/curl/lib/sockaddr.h index 2b03335087..916360d089 100644 --- a/deps/curl/lib/sockaddr.h +++ b/deps/curl/lib/sockaddr.h @@ -40,4 +40,22 @@ struct Curl_sockaddr_storage { } buffer; }; +/* + * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr + * structure with enough space available to directly hold any + * protocol-specific address structures. The variable declared here will be + * used to pass / receive data to/from the fopensocket callback if this has + * been set, before that, it is initialized from parameters. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr sa; + struct Curl_sockaddr_storage buf; + } addr; +}; + #endif /* HEADER_CURL_SOCKADDR_H */ diff --git a/deps/curl/lib/socketpair.c b/deps/curl/lib/socketpair.c index 76b959dd4a..39a0e319b0 100644 --- a/deps/curl/lib/socketpair.c +++ b/deps/curl/lib/socketpair.c @@ -232,16 +232,9 @@ static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) /* Do not block forever */ if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) goto error; - if( -#ifdef USE_WINSOCK - /* This is how Windows does it */ - (SOCKEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it - returned due to its inability to send off data without - blocking. We therefore treat both error codes the same here */ - (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || - (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) #endif ) { continue; @@ -309,7 +302,7 @@ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) int Curl_wakeup_signal(curl_socket_t socks[2]) { - int err = 0; + int sockerr = 0; #ifdef USE_EVENTFD const uint64_t buf[1] = { 1 }; #else @@ -317,22 +310,19 @@ int Curl_wakeup_signal(curl_socket_t socks[2]) #endif while(1) { - err = 0; + sockerr = 0; if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { - err = SOCKERRNO; -#ifdef USE_WINSOCK - if(err == SOCKEWOULDBLOCK) - err = 0; /* wakeup is already ongoing */ -#else - if(SOCKEINTR == err) + sockerr = SOCKERRNO; +#ifndef USE_WINSOCK + if(sockerr == SOCKEINTR) continue; - if((err == SOCKEWOULDBLOCK) || (err == EAGAIN)) - err = 0; /* wakeup is already ongoing */ #endif + if(SOCK_EAGAIN(sockerr)) + sockerr = 0; /* wakeup is already ongoing */ } break; } - return err; + return sockerr; } CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) @@ -346,15 +336,13 @@ CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) if(!rc) break; else if(rc < 0) { -#ifdef USE_WINSOCK - if(SOCKERRNO == SOCKEWOULDBLOCK) - break; -#else - if(SOCKEINTR == SOCKERRNO) + int sockerr = SOCKERRNO; +#ifndef USE_WINSOCK + if(sockerr == SOCKEINTR) continue; - if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN)) - break; #endif + if(SOCK_EAGAIN(sockerr)) + break; result = CURLE_READ_ERROR; break; } diff --git a/deps/curl/lib/socketpair.h b/deps/curl/lib/socketpair.h index 0427e72fc5..fd08c879da 100644 --- a/deps/curl/lib/socketpair.h +++ b/deps/curl/lib/socketpair.h @@ -27,7 +27,7 @@ #ifndef CURL_DISABLE_SOCKETPAIR -/* return < 0 for failure to initialise */ +/* return < 0 for failure to initialize */ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking); void Curl_wakeup_destroy(curl_socket_t socks[2]); diff --git a/deps/curl/lib/socks.c b/deps/curl/lib/socks.c index b3da5be0a3..6c458c505d 100644 --- a/deps/curl/lib/socks.c +++ b/deps/curl/lib/socks.c @@ -94,13 +94,11 @@ static const char * const cf_socks_statename[] = { #define SOCKS_CHUNK_SIZE 1024 #define SOCKS_CHUNKS 1 - struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - uint16_t remote_port; - const char *user; - const char *passwd; + struct Curl_peer *dest; + struct Curl_creds *creds; CURLproxycode presult; uint32_t resolv_id; uint8_t ip_version; @@ -109,7 +107,6 @@ struct socks_ctx { BIT(resolve_local); BIT(start_resolving); BIT(socks4a); - char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -273,8 +270,8 @@ static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, (void)data; buf[0] = 4; /* version (SOCKS4) */ buf[1] = 1; /* connect */ - buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ - buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ + buf[2] = (unsigned char)((sx->dest->port >> 8) & 0xffU); /* MSB */ + buf[3] = (unsigned char)(sx->dest->port & 0xffU); /* LSB */ result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); if(result || (nwritten != 4)) @@ -288,8 +285,8 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, CURLcode result; size_t nwritten; - if(sx->user) { - size_t plen = strlen(sx->user); + if(sx->creds) { + size_t plen = strlen(sx->creds->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -298,7 +295,7 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -329,7 +326,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -340,13 +337,13 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->hostname); + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) return CURLPX_OK; - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; @@ -365,7 +362,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, } else { /* No ipv4 address resolved */ - failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + failf(data, "SOCKS4 connection to %s not supported", sx->dest->hostname); return CURLPX_RESOLVE_HOST; } @@ -487,7 +484,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* SOCKS4 can only do IPv4, insist! */ sx->ip_version = CURL_IPRESOLVE_V4; CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", - sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); + sx->socks4a ? "a" : "", + sx->dest->hostname, sx->dest->port); /* * Compose socks4 request @@ -508,7 +506,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* socks4a, not resolving locally, sends the hostname. * add an invalid address + user + hostname */ unsigned char buf[4] = { 0, 0, 0, 1 }; - size_t hlen = strlen(sx->hostname) + 1; /* including NUL */ + size_t hlen = strlen(sx->dest->hostname) + 1; /* including NUL */ if(hlen > 255) { failf(data, "SOCKS4: too long hostname"); @@ -520,7 +518,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, presult = socks4_req_add_user(sx, data); if(presult) return socks_failed(sx, cf, data, presult); - result = Curl_bufq_cwrite(&sx->iobuf, sx->hostname, hlen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->dest->hostname, hlen, + &nwritten); if(result || (nwritten != hlen)) return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); /* request complete */ @@ -554,7 +553,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, FALLTHROUGH(); case SOCKS4_ST_RECV: - /* Receive 8 byte response */ + /* Receive 8-byte response */ presult = socks_recv(sx, cf, data, 8, &done); if(presult) return socks_failed(sx, cf, data, presult); @@ -591,7 +590,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, (void)cf; /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!sx->resolve_local && strlen(sx->hostname) > 255) { + if(!sx->resolve_local && strlen(sx->dest->hostname) > 255) { failf(data, "SOCKS5: the destination hostname is too long to be " "resolved remotely by the proxy."); return CURLPX_LONG_HOSTNAME; @@ -602,7 +601,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->user = NULL; + Curl_creds_unlink(&sx->creds); req[0] = 5; /* version */ nauths = 1; @@ -613,7 +612,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->user) { + if(sx->creds) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -657,8 +656,7 @@ static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT); return CURLPX_OK; } - failf(data, - "SOCKS5 GSSAPI per-message authentication is not enabled."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not enabled."); return CURLPX_GSSAPI_PERMSG; case 2: /* regular name + password authentication */ @@ -686,9 +684,9 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, unsigned char buf[2]; CURLcode result; - if(sx->user && sx->passwd) { - ulen = strlen(sx->user); - plen = strlen(sx->passwd); + if(sx->creds) { + ulen = strlen(sx->creds->user); + plen = strlen(sx->creds->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -713,7 +711,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -722,7 +720,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -779,29 +777,29 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, /* remote resolving, send what type+addr/string to resolve */ #ifdef USE_IPV6 - if(strchr(sx->hostname, ':')) { + if(strchr(sx->dest->hostname, ':')) { desttype = 4; destination = ipbuf; destlen = 16; - if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1) + if(curlx_inet_pton(AF_INET6, sx->dest->hostname, ipbuf) != 1) return CURLPX_BAD_ADDRESS_TYPE; } else #endif - if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) { + if(curlx_inet_pton(AF_INET, sx->dest->hostname, ipbuf) == 1) { desttype = 1; destination = ipbuf; destlen = 4; } else { - const size_t hostname_len = strlen(sx->hostname); + const size_t hostname_len = strlen(sx->dest->hostname); /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so this cast to unsigned char is safe. Assert to guard against future refactoring that might remove or reorder that earlier check. */ DEBUGASSERT(hostname_len <= 255); desttype = 3; - destination = (const unsigned char *)sx->hostname; - destlen = (unsigned char)hostname_len; /* one byte length */ + destination = (const unsigned char *)sx->dest->hostname; + destlen = (unsigned char)hostname_len; /* 1-byte length */ } req[3] = desttype; @@ -814,13 +812,13 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, if(result || (nwritten != destlen)) return CURLPX_SEND_REQUEST; /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xff); - req[1] = (unsigned char)(sx->remote_port & 0xff); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xff); + req[1] = (unsigned char)(sx->dest->port & 0xff); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", - sx->hostname, sx->remote_port); + sx->dest->hostname, sx->dest->port); return CURLPX_OK; } @@ -845,7 +843,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -855,7 +853,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, /* resolve the hostname by connecting the DNS filter */ result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) @@ -863,13 +862,14 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, #ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_V4) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET6, 0); #endif if(!ai) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(!ai) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } @@ -883,7 +883,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", - dest, sx->remote_port); + dest, sx->dest->port); } #ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) { @@ -893,7 +893,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", - dest, sx->remote_port); + dest, sx->dest->port); } #endif @@ -915,8 +915,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, goto out; } /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); - req[1] = (unsigned char)(sx->remote_port & 0xffU); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xffU); + req[1] = (unsigned char)(sx->dest->port & 0xffU); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) { presult = CURLPX_SEND_REQUEST; @@ -955,13 +955,13 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | + | 1 | 1 | 0x00 | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ ATYP: - o IP v4 address: X'01', BND.ADDR = 4 byte - o domain name: X'03', BND.ADDR = [ 1 byte length, string ] - o IP v6 address: X'04', BND.ADDR = 16 byte + o IPv4 address: 0x01, BND.ADDR = 4-byte + o domain name: 0x03, BND.ADDR = [ 1-byte length, string ] + o IPv6 address: 0x04, BND.ADDR = 16-byte */ if(resp[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); @@ -971,7 +971,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = resp[1]; failf(data, "cannot complete SOCKS5 connection to %s. (%d)", - sx->hostname, code); + sx->dest->hostname, code); if(code < 9) { /* RFC 1928 section 6 lists: */ static const CURLproxycode lookup[] = { @@ -1043,7 +1043,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_START: CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", - sx->hostname, sx->remote_port); + sx->dest->hostname, sx->dest->port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1075,7 +1075,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_GSSAPI_INIT: { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* GSSAPI stuff done non-blocking */ - CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data); + CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data, sx->creds); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -1083,8 +1083,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); goto process_state; #else - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG); #endif } @@ -1181,6 +1180,8 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, static void socks_proxy_ctx_free(struct socks_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->dest); + Curl_creds_unlink(&ctx->creds); Curl_bufq_free(&ctx->iobuf); curlx_free(ctx); } @@ -1244,7 +1245,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, "(via %s port %u)", (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - ctx->hostname, ctx->remote_port, + ctx->dest->hostname, ctx->dest->port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", @@ -1255,10 +1256,8 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, out: *done = (bool)cf->connected; - if(*done || result) { - ctx->user = NULL; - ctx->passwd = NULL; - } + if(*done || result) + Curl_creds_unlink(&ctx->creds); return result; } @@ -1278,11 +1277,11 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, case SOCKS5_ST_REQ0_SEND: case SOCKS5_ST_AUTH_SEND: case SOCKS5_ST_REQ1_SEND: - CURL_TRC_CF(data, cf, "adjust pollset out (%d)", sx->state); + CURL_TRC_CF(data, cf, "adjust pollset out (%d)", (int)sx->state); result = Curl_pollset_set_out_only(data, ps, sock); break; default: - CURL_TRC_CF(data, cf, "adjust pollset in (%d)", sx->state); + CURL_TRC_CF(data, cf, "adjust pollset in (%d)", (int)sx->state); result = Curl_pollset_set_in_only(data, ps, sock); break; } @@ -1290,14 +1289,6 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, return result; } -static void socks_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1315,8 +1306,8 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: if(sx) { - *pres1 = sx->remote_port; - *((const char **)pres2) = sx->hostname; + *pres1 = sx->dest->port; + *((const char **)pres2) = sx->dest->hostname; return CURLE_OK; } break; @@ -1340,7 +1331,6 @@ struct Curl_cftype Curl_cft_socks_proxy = { 0, socks_proxy_cf_destroy, socks_proxy_cf_connect, - socks_proxy_cf_close, Curl_cf_def_shutdown, socks_cf_adjust_pollset, Curl_cf_def_data_pending, @@ -1354,19 +1344,16 @@ struct Curl_cftype Curl_cft_socks_proxy = { CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd) + struct Curl_creds *creds) { struct Curl_cfilter *cf; struct socks_ctx *ctx; - size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - if(!hostlen) + if(!dest) return CURLE_FAILED_INIT; switch(proxy_type) { @@ -1381,17 +1368,15 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, } /* NUL byte already part of struct size */ - ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { return CURLE_OUT_OF_MEMORY; } - memcpy(ctx->hostname, hostname, hostlen); - ctx->remote_port = port; + Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; - ctx->user = user; - ctx->passwd = passwd; + Curl_creds_link(&ctx->creds, creds); Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, BUFQ_OPT_SOFT_LIMIT); diff --git a/deps/curl/lib/socks.h b/deps/curl/lib/socks.h index ea368326d2..d8e77c7f13 100644 --- a/deps/curl/lib/socks.h +++ b/deps/curl/lib/socks.h @@ -26,6 +26,10 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY + +struct Curl_peer; +struct Curl_creds; + /* * Helper read-from-socket functions. Does the same as Curl_read() but it * blocks until all bytes amount of buffersize will be read. No more, no less. @@ -43,22 +47,20 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, * This function handles the SOCKS5 GSS-API negotiation and initialization */ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_creds *creds); #endif -/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` - * and `port` with optional credentials. - * Credentials are NOT duplicated and are +/* Insert a SOCKS filter after `cf_at` for connecting to `dest`. + * Credentials are optional and NOT duplicated and are * expected to exist during connect phase. */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd); + struct Curl_creds *creds); extern struct Curl_cftype Curl_cft_socks_proxy; diff --git a/deps/curl/lib/socks_gssapi.c b/deps/curl/lib/socks_gssapi.c index 32db07044a..e10899c7e7 100644 --- a/deps/curl/lib/socks_gssapi.c +++ b/deps/curl/lib/socks_gssapi.c @@ -98,62 +98,33 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_gss_create_service_name(struct Curl_easy *data, + struct connectdata *conn, + const char *serviceptr, + gss_name_t *server) { - struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; - size_t actualread; - size_t nwritten; - CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; - OM_uint32 gss_ret_flags; - int gss_conf_state, gss_enc; - gss_buffer_desc service = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; - gss_name_t server = GSS_C_NO_NAME; - gss_name_t gss_client_name = GSS_C_NO_NAME; - unsigned short us_length; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ - const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - const size_t serviceptr_length = strlen(serviceptr); - gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + gss_buffer_desc service = GSS_C_EMPTY_BUFFER; /* prepare service name */ if(strchr(serviceptr, '/')) { - service.length = serviceptr_length; + service.length = strlen(serviceptr); service.value = curlx_memdup(serviceptr, service.length); if(!service.value) return CURLE_OUT_OF_MEMORY; gss_major_status = gss_import_name(&gss_minor_status, &service, - (gss_OID)GSS_C_NULL_OID, &server); + (gss_OID)GSS_C_NULL_OID, server); } else { - service.value = curlx_malloc(serviceptr_length + - strlen(conn->socks_proxy.host.name) + 2); + service.value = curl_maprintf("%s@%s", serviceptr, + conn->socks_proxy.peer->hostname); if(!service.value) return CURLE_OUT_OF_MEMORY; - service.length = serviceptr_length + - strlen(conn->socks_proxy.host.name) + 1; - curl_msnprintf(service.value, service.length + 1, "%s@%s", - serviceptr, conn->socks_proxy.host.name); + service.length = strlen(service.value); gss_major_status = gss_import_name(&gss_minor_status, &service, - GSS_C_NT_HOSTBASED_SERVICE, &server); + GSS_C_NT_HOSTBASED_SERVICE, server); } curlx_safefree(service.value); @@ -162,25 +133,50 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_import_name()")) { failf(data, "Failed to create service name."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server); return CURLE_COULDNT_CONNECT; } - (void)curlx_nonblock(sock, FALSE); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_name_t *server_ptr, + gss_ctx_id_t *gss_context, + OM_uint32 *gss_ret_flags) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + + /* GSS-API request looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ /* As long as we need to keep sending some context info, and there is no * errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, - &gss_context, - server, + gss_context, + *server_ptr, &Curl_krb5_mech_oid, NULL, gss_token, &gss_send_token, TRUE, - &gss_ret_flags); + gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) { curlx_safefree(gss_recv_token.value); @@ -190,10 +186,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_minor_status, "gss_init_sec_context") || /* the size needs to fit in a 16-bit field */ (gss_send_token.length > 0xffff)) { - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); - failf(data, "Failed to initial GSS-API token."); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); + failf(data, "Failed to initialize GSS-API token."); return CURLE_COULDNT_CONNECT; } @@ -203,23 +199,24 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((unsigned short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, + &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API authentication request."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } - code = Curl_conn_cf_send(cf->next, data, - gss_send_token.value, - gss_send_token.length, FALSE, &nwritten); - if(code || (gss_send_token.length != nwritten)) { + result = Curl_conn_cf_send(cf->next, data, + gss_send_token.value, + gss_send_token.length, FALSE, &nwritten); + if(result || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } @@ -241,8 +238,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -250,16 +247,16 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / message type */ failf(data, "Invalid GSS-API authentication response type (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -268,8 +265,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!us_length) { failf(data, "Invalid zero-length GSS-API authentication token."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -279,8 +276,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Could not allocate memory for GSS-API authentication " "response token."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } @@ -289,25 +286,34 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_verify(struct Curl_easy *data, + gss_ctx_id_t *gss_context) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_name_t gss_client_name = GSS_C_NO_NAME; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; /* Everything is good so far, user was authenticated! */ - gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, + gss_major_status = gss_inquire_context(&gss_minor_status, *gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_inquire_context")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; @@ -316,7 +322,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &gss_send_token, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_display_name")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine username."); @@ -329,6 +335,26 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); + return CURLE_OK; +} + +static CURLcode socks5_gss_negotiate_enc(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_ctx_id_t *gss_context, + OM_uint32 gss_ret_flags) +{ + struct connectdata *conn = cf->conn; + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + int gss_enc; + /* Do encryption */ socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 2; /* encryption message type */ @@ -383,19 +409,19 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_send_token.length = 1; gss_send_token.value = curlx_memdup(&gss_enc, gss_send_token.length); if(!gss_send_token.value) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, + gss_major_status = gss_wrap(&gss_minor_status, *gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, - &gss_conf_state, &gss_w_token); + NULL, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { curlx_safefree(gss_send_token.value); gss_send_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -406,30 +432,30 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); - if(code || (nwritten != 1)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); + if(result || (nwritten != 1)) { failf(data, "Failed to send GSS-API encryption type."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - code = Curl_conn_cf_send(cf->next, data, gss_w_token.value, - gss_w_token.length, FALSE, &nwritten); - if(code || (gss_w_token.length != nwritten)) { + result = Curl_conn_cf_send(cf->next, data, gss_w_token.value, + gss_w_token.length, FALSE, &nwritten); + if(result || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); @@ -438,7 +464,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -446,14 +472,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid GSS-API encryption response type (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -462,14 +488,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!us_length) { failf(data, "Invalid zero-length GSS-API encryption token."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, @@ -479,20 +505,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to receive GSS-API encryption type."); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { - gss_major_status = gss_unwrap(&gss_minor_status, gss_context, + gss_major_status = gss_unwrap(&gss_minor_status, *gss_context, &gss_recv_token, &gss_w_token, - 0, GSS_C_QOP_DEFAULT); + NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -503,7 +529,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API encryption response length (%zu).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -516,7 +542,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -525,20 +551,48 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length = 0; } - (void)curlx_nonblock(sock, TRUE); - - infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0) ? "out GSS-API data" : - ((socksreq[0] == 1) ? " GSS-API integrity" : - " GSS-API confidentiality")); + infof(data, "SOCKS5 access %s protection granted.", + (socksreq[0] == 0) ? "without GSS-API data" : + ((socksreq[0] == 1) ? "with GSS-API integrity" : + "with GSS-API confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OK; } +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_creds *creds) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + OM_uint32 gss_ret_flags = 0; + gss_name_t server = GSS_C_NO_NAME; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; + gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; + + result = socks5_gss_create_service_name(data, conn, service, &server); + if(!result) { + (void)curlx_nonblock(sock, FALSE); + result = socks5_gss_auth_loop(cf, data, &server, &gss_context, + &gss_ret_flags); + } + if(!result) + result = socks5_gss_auth_verify(data, &gss_context); + if(!result) + result = socks5_gss_negotiate_enc(cf, data, &gss_context, gss_ret_flags); + + /* unconditionally put it back to non-blocking */ + (void)curlx_nonblock(sock, TRUE); + + return result; +} + #if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/deps/curl/lib/socks_sspi.c b/deps/curl/lib/socks_sspi.c index 385312a368..10a8be1773 100644 --- a/deps/curl/lib/socks_sspi.c +++ b/deps/curl/lib/socks_sspi.c @@ -58,12 +58,13 @@ static int check_sspi_err(struct Curl_easy *data, /* This is the SSPI-using version of this function */ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_creds *creds, CredHandle *cred_handle, char **service_namep) { struct connectdata *conn = cf->conn; - const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; SECURITY_STATUS status; /* prepare service name */ @@ -71,7 +72,7 @@ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, *service_namep = curlx_strdup(service); else *service_namep = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); + service, conn->socks_proxy.peer->hostname); if(!*service_namep) return CURLE_OUT_OF_MEMORY; @@ -163,7 +164,7 @@ static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { - failf(data, "Failed to initialise security context."); + failf(data, "Failed to initialize security context."); return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } @@ -420,8 +421,7 @@ static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); - curlx_free(sspi_w_token[0].pvBuffer); - return result ? result : CURLE_COULDNT_CONNECT; + goto fail; } if(!data->set.socks5_gssapi_nec) { @@ -431,49 +431,48 @@ static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, sspi_w_token[1].cbBuffer = 0; sspi_w_token[1].pvBuffer = NULL; - status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, - 0, &qop); + /* At least one of the descriptors must be of type SECBUFFER_DATA. The + message is decrypted in place so the SECBUFFER_DATA receives a pointer + to the message in SECBUFFER_STREAM. */ + status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, 0, &qop); - if(check_sspi_err(data, status, "DecryptMessage")) { - if(sspi_w_token[1].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[0].pvBuffer); - return CURLE_COULDNT_CONNECT; - } + if(check_sspi_err(data, status, "DecryptMessage")) + goto fail; if(sspi_w_token[1].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[1].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[0].pvBuffer); - return CURLE_COULDNT_CONNECT; + goto fail; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); } else { if(sspi_w_token[0].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[0].cbBuffer); - curlx_free(sspi_w_token[0].pvBuffer); - return CURLE_COULDNT_CONNECT; + goto fail; } memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); } curlx_free(sspi_w_token[0].pvBuffer); - infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", - (socksreq[0] == 0) ? "out GSS-API data" : - ((socksreq[0] == 1) ? " GSS-API integrity" : - " GSS-API confidentiality")); + infof(data, "SOCKS5 access %s protection granted BUT NOT USED.", + (socksreq[0] == 0) ? "without GSS-API data" : + ((socksreq[0] == 1) ? "with GSS-API integrity" : + "with GSS-API confidentiality")); + cf->conn->socks5_gssapi_enctype = socksreq[0]; return CURLE_OK; + +fail: + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_creds *creds) { struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; @@ -489,7 +488,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memset(&sspi_context, 0, sizeof(sspi_context)); names.sUserName = NULL; - result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + result = socks5_sspi_setup(cf, data, creds, &cred_handle, &service_name); if(result) goto error; diff --git a/deps/curl/lib/strerror.c b/deps/curl/lib/strerror.c index 1e97c2829d..7b6dc02127 100644 --- a/deps/curl/lib/strerror.c +++ b/deps/curl/lib/strerror.c @@ -172,7 +172,7 @@ const char *curl_easy_strerror(CURLcode error) return "Can not set SSL crypto engine as default"; case CURLE_SSL_ENGINE_INITFAILED: - return "Failed to initialise SSL crypto engine"; + return "Failed to initialize SSL crypto engine"; case CURLE_SEND_ERROR: return "Failed sending data to the peer"; @@ -647,14 +647,15 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) "SEC_E_ILLEGAL_MESSAGE (0x%08lx) - This error usually " "occurs when a fatal SSL/TLS alert is received (e.g. " "handshake failed). More detail may be available in " - "the Windows System event log.", err); + "the Windows System event log.", (unsigned long)err); } else { char msgbuf[256]; if(curlx_get_winapi_error((DWORD)err, msgbuf, sizeof(msgbuf))) - curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, err, msgbuf); + curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, (unsigned long)err, + msgbuf); else - curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, err); + curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, (unsigned long)err); } #else /* CURLVERBOSE */ if(err == SEC_E_OK) diff --git a/deps/curl/lib/telnet.c b/deps/curl/lib/telnet.c index c5ce9c2c97..2870d7e2c9 100644 --- a/deps/curl/lib/telnet.c +++ b/deps/curl/lib/telnet.c @@ -53,6 +53,7 @@ #include "curl_trc.h" #include "progress.h" #include "arpa_telnet.h" +#include "connect.h" #include "select.h" #include "curlx/strparse.h" @@ -74,8 +75,8 @@ #define CURL_SB_LEN(x) ((x)->subend - (x)->subpointer) /* For posterity: -#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) -#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ +#define CURL_SB_PEEK(x) (*(x)->subpointer & 0xff) +#define CURL_SB_EOF(x) ((x)->subpointer >= (x)->subend) */ /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 @@ -195,20 +196,15 @@ static CURLcode init_telnet(struct Curl_easy *data) tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; - /* To be compliant with previous releases of libcurl - we enable this option by default. This behavior - can be changed thanks to the "BINARY" option in - CURLOPT_TELNETOPTIONS - */ + /* To be compliant with previous releases of libcurl we enable this option + by default. This behavior can be changed with the "BINARY" option in + CURLOPT_TELNETOPTIONS */ tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; - /* We must allow the server to echo what we sent - but it is not necessary to request the server - to do so (it might forces the server to close - the connection). Hence, we ignore ECHO in the - negotiate function - */ + /* We must allow the server to echo what we sent but it is not necessary + to request the server to do so (it might force the server to close + the connection). Hence, we ignore ECHO in the negotiate function */ tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; /* Set the subnegotiation fields to send information after negotiation @@ -216,12 +212,11 @@ static CURLcode init_telnet(struct Curl_easy *data) Default values are (0,0) initialized by calloc. According to the RFC1013 it is valid: - A value equal to zero is acceptable for the width (or height), - and means that no character width (or height) is being sent. - In this case, the width (or height) that will be assumed by the - Telnet server is operating system specific (it will probably be - based upon the terminal type information that may have been sent - using the TERMINAL TYPE Telnet option). */ + A value equal to zero is acceptable for the width (or height), and means + that no character width (or height) is being sent. In this case, the width + (or height) that will be assumed by the Telnet server is operating system + specific (it will probably be based upon the terminal type information + that may have been sent using the TERMINAL TYPE Telnet option). */ tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor); @@ -238,10 +233,8 @@ static void send_negotiation(struct Curl_easy *data, int cmd, int option) buf[2] = (unsigned char)option; bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); - if(bytes_written < 0) { - int err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); printoption(data, "SENT", cmd, option); } @@ -645,13 +638,19 @@ static CURLcode send_telnet_data(struct Curl_easy *data, while(!result && total_written < outlen) { /* Make sure socket is writable to avoid EWOULDBLOCK condition */ struct pollfd pfd[1]; + timediff_t timeout_ms = Curl_timeleft_ms(data); pfd[0].fd = conn->sock[FIRSTSOCKET]; pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { + if(timeout_ms < 0) + return CURLE_OPERATION_TIMEDOUT; + /* 0 means no timeout configured; pass -1 to poll for infinite wait */ + switch(Curl_poll(pfd, 1, timeout_ms ? timeout_ms : -1)) { case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ result = CURLE_SEND_ERROR; break; + case 0: /* timeout */ + result = CURLE_OPERATION_TIMEDOUT; + break; default: /* write! */ bytes_written = 0; result = Curl_xfer_send(data, outbuf + total_written, @@ -673,7 +672,6 @@ static void sendsuboption(struct Curl_easy *data, struct TELNET *tn, int option) { ssize_t bytes_written; - int err; unsigned short x, y; const unsigned char *uc1, *uc2; struct connectdata *conn = data->conn; @@ -706,19 +704,15 @@ static void sendsuboption(struct Curl_easy *data, /* we send the header of the suboption... */ bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); /* ... then the window size with the send_telnet_data() function to deal with 0xFF cases ... */ send_telnet_data(data, tn, (const char *)tn->subbuffer + 3, 4); /* ... and the footer */ bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); break; } } @@ -773,7 +767,7 @@ static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) break; case CURL_OPPOSITE: tn->us[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; + tn->usq[option] = CURL_EMPTY; send_negotiation(data, CURL_WONT, option); break; } @@ -840,13 +834,14 @@ static CURLcode check_telnet_options(struct Curl_easy *data, /* Add the username as an environment variable if it was given on the command line */ - if(data->state.aptr.user) { + if(data->state.creds) { char buffer[256]; - if(str_is_nonascii(data->conn->user)) { + if(str_is_nonascii(Curl_creds_user(data->conn->creds))) { DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } - curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + curl_msnprintf(buffer, sizeof(buffer), "USER,%s", + Curl_creds_user(data->conn->creds)); beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); @@ -979,7 +974,6 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) unsigned char temp[2048]; ssize_t bytes_written; size_t len; - int err; struct connectdata *conn = data->conn; if(!CURL_SB_LEN(tn)) /* ignore empty suboption */ @@ -992,7 +986,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(tn->subopt_ttype)) return CURLE_BAD_FUNCTION_ARGUMENT; if(strlen(tn->subopt_ttype) > 1000) { - failf(data, "Tool long telnet TTYPE"); + failf(data, "Too long telnet TTYPE"); return CURLE_SEND_ERROR; } len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", @@ -1002,8 +996,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); + failf(data, "Sending data failed (%d)", SOCKERRNO); return CURLE_SEND_ERROR; } printsub(data, '>', &temp[2], len-2); @@ -1012,7 +1005,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(tn->subopt_xdisploc)) return CURLE_BAD_FUNCTION_ARGUMENT; if(strlen(tn->subopt_xdisploc) > 1000) { - failf(data, "Tool long telnet XDISPLOC"); + failf(data, "Too long telnet XDISPLOC"); return CURLE_SEND_ERROR; } len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", @@ -1021,8 +1014,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) CURL_SE); bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); + failf(data, "Sending data failed (%d)", SOCKERRNO); return CURLE_SEND_ERROR; } printsub(data, '>', &temp[2], len - 2); @@ -1036,7 +1028,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(v->data)) return CURLE_BAD_FUNCTION_ARGUMENT; /* Add the variable if it fits */ - if(len + tmplen < (int)sizeof(temp) - 6) { + if(len + tmplen < sizeof(temp) - 6) { const char *s = strchr(v->data, ','); if(!s) len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len, @@ -1053,10 +1045,8 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) "%c%c", CURL_IAC, CURL_SE); len += 2; bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); printsub(data, '>', &temp[2], len - 2); break; } @@ -1239,7 +1229,6 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) DWORD obj_count; DWORD wait_timeout; DWORD readfile_read; - int err; #else timediff_t interval_ms; struct pollfd pfd[2]; @@ -1283,7 +1272,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) return CURLE_RECV_ERROR; } - /* The get the Windows file handle for stdin */ + /* Then get the Windows file handle for stdin */ stdin_handle = GetStdHandle(STD_INPUT_HANDLE); /* Create the list of objects to wait for */ @@ -1377,9 +1366,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) case WAIT_OBJECT_0: { events.lNetworkEvents = 0; if(WSAEnumNetworkEvents(sockfd, event_handle, &events) != 0) { - err = SOCKERRNO; - if(err != SOCKEINPROGRESS) { - infof(data, "WSAEnumNetworkEvents failed (%d)", err); + int sockerr = SOCKERRNO; + if(sockerr != SOCKEINPROGRESS) { + infof(data, "WSAEnumNetworkEvents failed (%d)", sockerr); keepon = FALSE; result = CURLE_READ_ERROR; } diff --git a/deps/curl/lib/tftp.c b/deps/curl/lib/tftp.c index 6cc672d447..00e89752bd 100644 --- a/deps/curl/lib/tftp.c +++ b/deps/curl/lib/tftp.c @@ -167,7 +167,8 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) } /* Set per-block timeout to total */ - if(timeout_ms > 0) + if((timeout_ms > 0) && (timeout_ms < 3600000)) + /* do the calculation only if the timeout is "reasonable" */ timeout = (time_t)(timeout_ms + 500) / 1000; else timeout = 15; @@ -266,16 +267,19 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, while(tmp < ptr + len) { const char *option, *value; + size_t olen; tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); if(!tmp) { failf(data, "Malformed ACK packet, rejecting"); return CURLE_TFTP_ILLEGAL; } + olen = strlen(option); infof(data, "got option=(%s) value=(%s)", option, value); - if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { + if((strlen(TFTP_OPTION_BLKSIZE) == olen) && + checkprefix(TFTP_OPTION_BLKSIZE, option)) { curl_off_t blksize; if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) { failf(data, "%s (%d)", "blksize is larger than max supported", @@ -304,7 +308,8 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } - else if(checkprefix(TFTP_OPTION_TSIZE, option)) { + else if((strlen(TFTP_OPTION_TSIZE) == olen) && + checkprefix(TFTP_OPTION_TSIZE, option)) { curl_off_t tsize = 0; /* tsize should be ignored on upload: Who cares about the size of the remote file? */ @@ -486,7 +491,7 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) break; default: - failf(data, "tftp_tx: internal error, event: %i", (int)event); + failf(data, "tftp_tx: internal error, event: %d", (int)event); break; } @@ -671,7 +676,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } if(data->state.upload) { - /* If we are uploading, send an WRQ */ + /* If we are uploading, send a WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); if(data->state.infilesize != -1) Curl_pgrsSetUploadSize(data, data->state.infilesize); @@ -736,7 +741,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } } - /* the typecase for the 3rd argument is mostly for systems that do + /* the typecast for the 3rd argument is mostly for systems that do not have a size_t argument, like older unixes that want an 'int' */ #ifdef __AMIGA__ #define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) @@ -869,7 +874,7 @@ static CURLcode tftp_state_machine(struct tftp_conn *state, infof(data, "%s", "TFTP finished"); break; default: - DEBUGF(infof(data, "STATE: %d", state->state)); + DEBUGF(infof(data, "STATE: %d", (int)state->state)); failf(data, "%s", "Internal state machine error"); result = CURLE_TFTP_ILLEGAL; break; @@ -1192,9 +1197,9 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) if(rc == -1) { /* bail out */ - int error = SOCKERRNO; + int sockerr = SOCKERRNO; char buffer[STRERROR_LEN]; - failf(data, "%s", curlx_strerror(error, buffer, sizeof(buffer))); + failf(data, "%s", curlx_strerror(sockerr, buffer, sizeof(buffer))); state->event = TFTP_EVENT_ERROR; } else if(rc) { diff --git a/deps/curl/lib/thrdpool.c b/deps/curl/lib/thrdpool.c index 22faa396ed..e8c7d36435 100644 --- a/deps/curl/lib/thrdpool.c +++ b/deps/curl/lib/thrdpool.c @@ -130,9 +130,9 @@ static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) * on activating threads that have no means to shut down. */ if((tpool->idle_time_ms > 0) && (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { - CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock, - tpool->idle_time_ms); - if((r == CURLE_OPERATION_TIMEDOUT) && + CURLcode result = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((result == CURLE_OPERATION_TIMEDOUT) && (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { goto out; } diff --git a/deps/curl/lib/thrdqueue.c b/deps/curl/lib/thrdqueue.c index 1521ccfbee..f68f8e1797 100644 --- a/deps/curl/lib/thrdqueue.c +++ b/deps/curl/lib/thrdqueue.c @@ -351,11 +351,16 @@ void Curl_thrdq_clear(struct curl_thrdq *tqueue, Curl_mutex_release(&tqueue->lock); } -CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, - uint32_t timeout_ms) +#ifdef UNITTESTS +/* @unittest 3301 */ +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) { return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); } +#endif CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, uint32_t max_len, diff --git a/deps/curl/lib/thrdqueue.h b/deps/curl/lib/thrdqueue.h index d267f5d091..1144bb4185 100644 --- a/deps/curl/lib/thrdqueue.h +++ b/deps/curl/lib/thrdqueue.h @@ -26,23 +26,24 @@ #include "curl_setup.h" #include "curlx/timediff.h" +struct curl_thrdq; + #ifdef USE_THREADS struct Curl_easy; -struct curl_thrdq; typedef enum { CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ } Curl_thrdq_event; -/* Notification callback when "events" happen in the queue. May be - * call from any thread, queue is not locked. */ +/* Notification callback when "events" happen in the queue. May be called from + * any thread, queue is not locked. */ typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue, Curl_thrdq_event ev, void *user_data); -/* Process a queued item. Maybe call from any thread. Queue is - * not locked. */ +/* Process a queued item. May be called from any thread. Queue is not + * locked. */ typedef void Curl_thrdq_item_process_cb(void *item); /* Free an item. May be called from any thread at any time for an diff --git a/deps/curl/lib/transfer.c b/deps/curl/lib/transfer.c index fd1a903dab..1a4138f6c1 100644 --- a/deps/curl/lib/transfer.c +++ b/deps/curl/lib/transfer.c @@ -332,7 +332,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, out: Curl_multi_xfer_buf_release(data, xfer_buf); if(result) - DEBUGF(infof(data, "sendrecv_dl() -> %d", result)); + DEBUGF(infof(data, "sendrecv_dl() -> %d", (int)result)); return result; } @@ -425,7 +425,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) out: if(result) - DEBUGF(infof(data, "Curl_sendrecv() -> %d", result)); + DEBUGF(infof(data, "Curl_sendrecv() -> %d", (int)result)); return result; } @@ -438,40 +438,6 @@ void Curl_init_CONNECT(struct Curl_easy *data) data->state.upload = (data->state.httpreq == HTTPREQ_PUT); } -/* - * Restore the user credentials to those set in options. - */ -CURLcode Curl_reset_userpwd(struct Curl_easy *data) -{ - CURLcode result; - if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); - return result; -} - -/* - * Restore the proxy credentials to those set in options. - */ -CURLcode Curl_reset_proxypwd(struct Curl_easy *data) -{ -#ifndef CURL_DISABLE_PROXY - CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); - return result; -#else - (void)data; - return CURLE_OK; -#endif -} - /* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass @@ -524,6 +490,10 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #endif data->state.httpreq = data->set.method; + /* initial transfer request coming up, forget the initial origin + * from a previous perform() on this handle. */ + Curl_peer_unlink(&data->state.initial_origin); + Curl_peer_unlink(&data->state.origin); data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ @@ -542,6 +512,8 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(data->set.str[STRING_SET_REFERER]) Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], 0, NULL); + else + Curl_bufref_free(&data->state.referer); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -625,11 +597,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } - if(!result) - result = Curl_reset_userpwd(data); - if(!result) - result = Curl_reset_proxypwd(data); - data->req.headerbytecount = 0; Curl_headers_cleanup(data); return result; @@ -712,7 +679,7 @@ static void xfer_setup( struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; - DEBUGASSERT(conn != NULL); + DEBUGASSERT(conn); /* indexes are in range */ DEBUGASSERT((send_idx <= 1) && (send_idx >= -1)); DEBUGASSERT((recv_idx <= 1) && (recv_idx >= -1)); @@ -811,7 +778,7 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, data->req.download_done = TRUE; } CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d", - blen, is_eos, result); + blen, is_eos, (int)result); return result; } @@ -868,7 +835,7 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, data->info.request_size += *pnwritten; DEBUGF(infof(data, "Curl_xfer_send(len=%zu, eos=%d) -> %d, %zu", - blen, eos, result, *pnwritten)); + blen, eos, (int)result, *pnwritten)); return result; } @@ -936,22 +903,14 @@ CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable) bool Curl_xfer_is_secure(struct Curl_easy *data) { - const struct Curl_scheme *scheme = NULL; - - if(data->conn) { - scheme = data->conn->scheme; - /* if we are connected, but not use SSL, the transfer is not secure. - * This covers an insecure http:// proxy that is not tunneling. - * We enforce tunneling for such cases, but better be sure here. */ - if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) && - !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) - return FALSE; - } - else if(data->info.conn_scheme) { /* was connected once */ - scheme = Curl_get_scheme(data->info.conn_scheme); - } - else { /* never connected (yet?) */ - DEBUGASSERT(0); /* not implemented, would need to parse URL */ +#ifndef CURL_DISABLE_PROXY + if(data->conn && data->conn->bits.origin_is_proxy) { + /* talking to a forward proxy, not secure. we do not use + * a forward proxy for https: and other 's' URLs. Let's just check that + * this did not fail somewhere. */ + DEBUGASSERT(!(data->state.origin->scheme->flags & PROTOPT_SSL)); + return FALSE; } - return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; +#endif + return (data->state.origin->scheme->flags & PROTOPT_SSL); } diff --git a/deps/curl/lib/transfer.h b/deps/curl/lib/transfer.h index b29e70b9ec..7507ce27bd 100644 --- a/deps/curl/lib/transfer.h +++ b/deps/curl/lib/transfer.h @@ -31,8 +31,6 @@ char *Curl_checkheaders(const struct Curl_easy *data, void Curl_init_CONNECT(struct Curl_easy *data); -CURLcode Curl_reset_userpwd(struct Curl_easy *data); -CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data); @@ -145,8 +143,8 @@ bool Curl_xfer_recv_is_paused(struct Curl_easy *data); CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); -/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the - * URL's host. */ +/* TRUE if the transfer is secure, e.g. uses TLS and does not + * use a forward proxy. */ bool Curl_xfer_is_secure(struct Curl_easy *data); #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/deps/curl/lib/uint-bset.c b/deps/curl/lib/uint-bset.c index 5469174944..55aedb234b 100644 --- a/deps/curl/lib/uint-bset.c +++ b/deps/curl/lib/uint-bset.c @@ -157,7 +157,7 @@ bool Curl_uint32_bset_next(struct uint32_bset *bset, uint32_t last, /* shift away the bits we already iterated in this slot */ x = (bset->slots[islot] >> (last % 64)); if(x) { - /* more bits set, next is `last` + trailing0s of the shifted slot */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } @@ -179,10 +179,10 @@ uint32_t Curl_popcount64(uint64_t x) /* Compute the "Hamming Distance" between 'x' and 0, * which is the number of set bits in 'x'. * See: https://en.wikipedia.org/wiki/Hamming_weight */ - const uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ - const uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ - const uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ - /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ + const uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ + const uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ + const uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ + /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ const uint64_t h01 = 0x0101010101010101LL; x -= (x >> 1) & m1; /* replace every 2 bits with bits present */ x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */ diff --git a/deps/curl/lib/uint-spbset.c b/deps/curl/lib/uint-spbset.c index 3daa2eea75..d4e6c8e70b 100644 --- a/deps/curl/lib/uint-spbset.c +++ b/deps/curl/lib/uint-spbset.c @@ -203,7 +203,7 @@ static bool uint32_spbset_chunk_next(struct uint32_spbset_chunk *chunk, if(i < CURL_UINT32_SPBSET_CH_SLOTS) { x = (chunk->slots[i] >> (last % 64)); if(x) { - /* more bits set, next is `last` + trailing0s of the shifted slot */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } diff --git a/deps/curl/lib/url.c b/deps/curl/lib/url.c index 5fe68033d9..505e08a7e1 100644 --- a/deps/curl/lib/url.c +++ b/deps/curl/lib/url.c @@ -86,7 +86,7 @@ #include "urlapi-int.h" #include "system_win32.h" #include "hsts.h" -#include "noproxy.h" +#include "proxy.h" #include "cfilters.h" #include "idn.h" #include "http_proxy.h" @@ -99,6 +99,7 @@ #include "headers.h" #include "curlx/strerr.h" #include "curlx/strparse.h" +#include "peer.h" /* Now for the protocols */ #include "ftp.h" @@ -106,6 +107,7 @@ #include "telnet.h" #include "tftp.h" #include "http.h" +#include "vauth/vauth.h" #include "file.h" #include "curl_ldap.h" #include "vssh/ssh.h" @@ -118,12 +120,6 @@ #include "smtp.h" #include "ws.h" -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data); -#else -#define data_priority_cleanup(x) -#endif - /* Some parts of the code (e.g. chunked encoding) assume this buffer has more * than a few bytes to play with. Do not let it become too small or bad things * will happen. @@ -132,9 +128,6 @@ static void data_priority_cleanup(struct Curl_easy *data); # error READBUFFER_SIZE is too small #endif -/* Reject URLs exceeding this length */ -#define MAX_URL_LEN 0xffff - /* * get_protocol_family() * @@ -252,7 +245,8 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - curlx_safefree(data->state.first_host); + Curl_peer_unlink(&data->state.origin); + Curl_peer_unlink(&data->state.initial_origin); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -277,13 +271,12 @@ CURLcode Curl_close(struct Curl_easy **datap) curlx_safefree(data->info.contenttype); curlx_safefree(data->info.wouldredirect); - data_priority_cleanup(data); - /* No longer a dirty share, if it exists */ if(Curl_share_easy_unlink(data)) DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); + Curl_creds_unlink(&data->state.creds); curlx_safefree(data->state.aptr.uagent); curlx_safefree(data->state.aptr.accept_encoding); curlx_safefree(data->state.aptr.rangeline); @@ -295,12 +288,6 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_RTSP curlx_safefree(data->state.aptr.rtsp_transport); #endif - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); -#ifndef CURL_DISABLE_PROXY - curlx_safefree(data->state.aptr.proxyuser); - curlx_safefree(data->state.aptr.proxypasswd); -#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); @@ -312,6 +299,13 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_freeset(data); Curl_headers_cleanup(data); Curl_netrc_cleanup(&data->state.netrc); +#ifndef CURL_DISABLE_DIGEST_AUTH + curlx_free(data->state.envproxy); +#endif + Curl_ssl_config_cleanup(&data->set.ssl.primary); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&data->set.proxy_ssl.primary); +#endif curlx_free(data); return CURLE_OK; } @@ -366,7 +360,9 @@ void Curl_init_userdefined(struct Curl_easy *data) set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ + Curl_ssl_config_init(&data->set.ssl.primary); #ifndef CURL_DISABLE_PROXY + Curl_ssl_config_init(&data->set.proxy_ssl.primary); set->proxyport = 0; set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ @@ -374,7 +370,6 @@ void Curl_init_userdefined(struct Curl_easy *data) set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; #endif - Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; @@ -448,7 +443,7 @@ static void easy_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } /** @@ -513,34 +508,24 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_conn_cf_discard_all(data, conn, (int)i); } - Curl_free_idnconverted_hostname(&conn->host); - Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY - Curl_free_idnconverted_hostname(&conn->http_proxy.host); - Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - curlx_safefree(conn->http_proxy.user); - curlx_safefree(conn->socks_proxy.user); - curlx_safefree(conn->http_proxy.passwd); - curlx_safefree(conn->socks_proxy.passwd); - curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ - curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ -#endif - curlx_safefree(conn->user); - curlx_safefree(conn->passwd); - curlx_safefree(conn->sasl_authzid); + Curl_peer_unlink(&conn->http_proxy.peer); + Curl_peer_unlink(&conn->socks_proxy.peer); + Curl_creds_unlink(&conn->http_proxy.creds); + Curl_creds_unlink(&conn->socks_proxy.creds); +#endif + Curl_creds_unlink(&conn->creds); + Curl_peer_unlink(&conn->creds_origin); curlx_safefree(conn->options); - curlx_safefree(conn->oauth_bearer); - curlx_safefree(conn->host.rawalloc); /* hostname buffer */ - curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ - curlx_safefree(conn->secondaryhostname); curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); -#ifdef USE_UNIX_SOCKETS - curlx_safefree(conn->unix_domain_socket); -#endif curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); + Curl_peer_unlink(&conn->origin); + Curl_peer_unlink(&conn->via_peer); + Curl_peer_unlink(&conn->origin2); + Curl_peer_unlink(&conn->via_peer2); curlx_free(conn); /* free all the connection oriented data */ } @@ -576,12 +561,8 @@ static bool proxy_info_matches(const struct proxy_info *data, const struct proxy_info *needle) { if((data->proxytype == needle->proxytype) && - (data->port == needle->port) && - curl_strequal(data->host.name, needle->host.name)) { - - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; + Curl_peer_same_destination(data->peer, needle->peer) && + Curl_creds_same(data->creds, needle->creds)) { return TRUE; } return FALSE; @@ -651,7 +632,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, Curl_attach_connection(data, conn); dead = !Curl_conn_is_alive(data, conn, &input_pending); if(input_pending) { - /* For reuse, we want a "clean" connection state. The includes + /* For reuse, we want a "clean" connection state. This includes * that we expect - in general - no waiting input data. Input * waiting might be a TLS Notify Close, for example. We reject * that. @@ -714,7 +695,11 @@ struct url_conn_match { BIT(want_proxy_ntlm_http); BIT(want_nego_http); BIT(want_proxy_nego_http); - BIT(req_tls); /* require TLS use from a clear-text start */ + BIT(may_tls); /* May upgrade clear-text connection to TLS, can only reuse + * connections that have matching TLS configuration. + * Always TRUE if `req_tls` is TRUE. */ + BIT(require_tls); /* Requires TLS use from a clear-text start, can only + * reuse connections that have TLS. */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -753,30 +738,11 @@ static bool url_match_connect_config(struct connectdata *conn, return FALSE; } - if(m->needle->bits.conn_to_host != conn->bits.conn_to_host) + if(!m->needle->via_peer != !conn->via_peer) /* do not mix connections that use the "connect to host" feature and * connections that do not use this feature */ return FALSE; - if(m->needle->bits.conn_to_port != conn->bits.conn_to_port) - /* do not mix connections that use the "connect to port" feature and - * connections that do not use this feature */ - return FALSE; - - /* Does `conn` use the correct protocol? */ -#ifdef USE_UNIX_SOCKETS - if(m->needle->unix_domain_socket) { - if(!conn->unix_domain_socket) - return FALSE; - if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket)) - return FALSE; - if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket) - return FALSE; - } - else if(conn->unix_domain_socket) - return FALSE; -#endif - return TRUE; } @@ -866,7 +832,7 @@ static bool url_match_ssl_use(struct connectdata *conn, (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } - else if(m->req_tls) + else if(m->require_tls) /* a clear-text STARTTLS protocol with required TLS */ return FALSE; return TRUE; @@ -876,36 +842,27 @@ static bool url_match_ssl_use(struct connectdata *conn, static bool url_match_proxy_use(struct connectdata *conn, struct url_conn_match *m) { - if(m->needle->bits.httpproxy != conn->bits.httpproxy || - m->needle->bits.socksproxy != conn->bits.socksproxy) + if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy) return FALSE; - if(m->needle->bits.socksproxy && - !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) + if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) return FALSE; - if(m->needle->bits.httpproxy) { - if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy) - return FALSE; + if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) + return FALSE; - if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) + if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + /* match SSL config to proxy */ + if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) { + DEBUGF(infof(m->data, + "Connection #%" FMT_OFF_T + " has different SSL proxy parameters, cannot reuse", + conn->connection_id)); return FALSE; - - if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) { - /* https proxies come in different types, http/1.1, h2, ... */ - if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype) - return FALSE; - /* match SSL config to proxy */ - if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) { - DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T - " has different SSL proxy parameters, cannot reuse", - conn->connection_id)); - return FALSE; - } - /* the SSL config to the server, which may apply here is checked - * further below */ } + /* the SSL config to the server, which may apply here is checked + * further below */ } return TRUE; } @@ -994,20 +951,15 @@ static bool url_match_proto_config(struct connectdata *conn, static bool url_match_auth(struct connectdata *conn, struct url_conn_match *m) { - if(!(m->needle->scheme->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we are using the same name and password as well */ - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd) || - Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) || - Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) { - /* one of them was different */ + if(!Curl_creds_same(m->needle->creds, conn->creds)) { + if(m->needle->creds) + return FALSE; + if(!Curl_creds_same(m->data->state.creds, conn->creds)) return FALSE; - } } -#ifdef HAVE_GSSAPI - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS delegation differences do not actually affect every connection and + auth method, but this check takes precaution before efficiency */ if(m->needle->gssapi_delegation != conn->gssapi_delegation) return FALSE; #endif @@ -1018,49 +970,29 @@ static bool url_match_auth(struct connectdata *conn, static bool url_match_destination(struct connectdata *conn, struct url_conn_match *m) { - /* Additional match requirements if talking TLS OR - * not talking to an HTTP proxy OR using a tunnel through a proxy */ - if((m->needle->scheme->flags & PROTOPT_SSL) -#ifndef CURL_DISABLE_PROXY - || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy -#endif - ) { - if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) { - /* `needle` and `conn` do not have the same scheme... */ - if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { - /* and `conn`s protocol family is not the protocol `needle` wants. - * IMAPS would work for IMAP, but no vice versa. */ - return FALSE; - } - /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */ - if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T - " has compatible protocol family, but no SSL, no match", - conn->connection_id)); - return FALSE; - } - } - - /* If needle has "conn_to_*" set, conn must match this */ - if((m->needle->bits.conn_to_host && !curl_strequal( - m->needle->conn_to_host.name, conn->conn_to_host.name)) || - (m->needle->bits.conn_to_port && - m->needle->conn_to_port != conn->conn_to_port)) - return FALSE; + /* Different connect-to peers never match */ + if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) + return FALSE; - /* hostname and port must match */ - if(!curl_strequal(m->needle->host.name, conn->host.name) || - m->needle->remote_port != conn->remote_port) + if(m->needle->origin->scheme != conn->origin->scheme) { + /* `needle` and `conn` not having the same scheme. + * This is allowed for the same family *if* conn is using TLS. + * - IMAP+STARTTLS works for IMAPS. + * - IMAPS works for IMAP. */ + if(get_protocol_family(conn->origin->scheme) != + m->needle->scheme->protocol) { return FALSE; + } } - return TRUE; + /* Scheme mismatch is acceptable, compare hostname/port */ + return Curl_peer_same_destination(m->needle->origin, conn->origin); } static bool url_match_ssl_config(struct connectdata *conn, struct url_conn_match *m) { - /* If talking TLS, conn needs to use the same SSL options. */ - if((m->needle->scheme->flags & PROTOPT_SSL) && + /* If talking/upgrading to TLS, conn needs to use the same SSL options. */ + if(((m->needle->scheme->flags & PROTOPT_SSL) || m->may_tls) && !Curl_ssl_conn_config_match(m->data, conn, FALSE)) { DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T " has different SSL parameters, cannot reuse", @@ -1080,19 +1012,11 @@ static bool url_match_auth_ntlm(struct connectdata *conn, possible. (Especially we must not reuse the same connection if partway through a handshake!) */ if(m->want_ntlm_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) { - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM if it does - not have any auth ongoing. */ -#ifdef USE_SPNEGO - if((conn->http_ntlm_state == NTLMSTATE_NONE) - && (conn->http_negotiate_state == GSS_AUTHNONE)) { -#else - if(conn->http_ntlm_state == NTLMSTATE_NONE) { -#endif - m->found = conn; - } + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) { + /* connection credentials in play and not the same or not for the + * same origin. */ return FALSE; } } @@ -1106,13 +1030,10 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(m->want_proxy_ntlm_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { @@ -1152,8 +1073,9 @@ static bool url_match_auth_nego(struct connectdata *conn, already authenticating with the right credentials. If not, keep looking so that we can reuse Negotiate connections if possible. */ if(m->want_nego_http) { - if(Curl_timestrcmp(m->needle->user, conn->user) || - Curl_timestrcmp(m->needle->passwd, conn->passwd)) + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) return FALSE; } else if(conn->http_negotiate_state != GSS_AUTHNONE) { @@ -1166,13 +1088,10 @@ static bool url_match_auth_nego(struct connectdata *conn, if(m->want_proxy_nego_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) return FALSE; - if(Curl_timestrcmp(m->needle->http_proxy.user, - conn->http_proxy.user) || - Curl_timestrcmp(m->needle->http_proxy.passwd, - conn->http_proxy.passwd)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { @@ -1222,6 +1141,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_ssl_use(conn, m)) return FALSE; + if(!url_match_proxy_use(conn, m)) return FALSE; if(!url_match_ssl_config(conn, m)) @@ -1315,7 +1235,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NTLM) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1327,12 +1247,13 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_nego_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif - match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.may_tls = data->set.use_ssl > CURLUSESSL_NONE; /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ @@ -1362,7 +1283,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ conn->created = *Curl_pgrs_now(data); @@ -1370,29 +1290,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) /* Store current time to give a baseline to keepalive connection times. */ conn->keepalive = conn->created; -#ifndef CURL_DISABLE_PROXY - conn->http_proxy.proxytype = data->set.proxytype; - conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; - - /* note that these two proxy bits are set on what looks to be - requested, they may be altered down the road */ - conn->bits.proxy = (data->set.str[STRING_PROXY] && - *data->set.str[STRING_PROXY]); - conn->bits.httpproxy = (conn->bits.proxy && - (conn->http_proxy.proxytype == CURLPROXY_HTTP || - conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || - IS_HTTPS_PROXY(conn->http_proxy.proxytype))); - conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy); - - if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { - conn->bits.proxy = TRUE; - conn->bits.socksproxy = TRUE; - } - - conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser; - conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; -#endif /* CURL_DISABLE_PROXY */ - #ifndef CURL_DISABLE_FTP conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; @@ -1417,10 +1314,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; conn->lastused = conn->created; -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) conn->gssapi_delegation = data->set.gssapi_delegation; #endif - DEBUGF(infof(data, "alloc connection, bits.close=%d", conn->bits.close)); return conn; error: @@ -1429,36 +1325,25 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) return NULL; } -static CURLcode findprotocol(struct Curl_easy *data, - struct connectdata *conn, - const char *protostr) +static CURLcode url_set_conn_scheme(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_scheme *scheme) { - const struct Curl_scheme *p = Curl_get_scheme(protostr); - - if(p && p->run && /* Protocol found supported. Check if allowed */ - (data->set.allowed_protocols & p->protocol)) { - - /* it is allowed for "normal" request, now do an extra check if this is - the result of a redirect */ - if(data->state.this_is_a_follow && - !(data->set.redir_protocols & p->protocol)) - /* nope, get out */ - ; - else { - /* Perform setup complement if some. */ - conn->scheme = conn->given = p; - /* 'port' and 'remote_port' are set in setup_connection_internals() */ - return CURLE_OK; - } + /* URL scheme is usable for connection when it is + * - allowed + * - not from a redirect or an allowed redirect protocol */ + if(scheme->run && + (data->set.allowed_protocols & scheme->protocol) && + (!data->state.this_is_a_follow || + (data->set.redir_protocols & scheme->protocol))) { + conn->scheme = conn->given = scheme; + return CURLE_OK; } - - /* The protocol was not found in the table, but we do not have to assign it - to anything since it is already assigned to a dummy-struct in the - create_conn() function when the connectdata struct is allocated. */ - failf(data, "Protocol \"%s\" %s%s", protostr, - p ? "disabled" : "not supported", - data->state.this_is_a_follow ? " (in redirect)" : ""); - + if(scheme->flags & PROTOPT_NO_TRANSFER) + failf(data, "Protocol \"%s\" is not for transfers", scheme->name); + else + failf(data, "Protocol \"%s\" is disabled%s", scheme->name, + data->state.this_is_a_follow ? " (in redirect)" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -1476,269 +1361,273 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } } -#ifdef USE_IPV6 -/* - * If the URL was set with an IPv6 numerical address with a zone id part, set - * the scope_id based on that! - */ - -static void zonefrom_url(CURLU *uh, struct Curl_easy *data, - struct connectdata *conn) +#ifndef CURL_DISABLE_HSTS +static CURLcode hsts_upgrade(struct Curl_easy *data, + CURLU *uh, + uint16_t port_override, + uint32_t scope_id) { - char *zoneid; - CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) - (void)data; -#endif + /* HSTS upgrade */ + if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) && + Curl_hsts_applies(data->hsts, data->state.origin)) { + char *url; + CURLUcode uc; + CURLcode result; - if(!uc && zoneid) { - const char *p = zoneid; - curl_off_t scope; - if(!curlx_str_number(&p, &scope, UINT_MAX)) - /* A plain number, use it directly as a scope id. */ - conn->scope_id = (unsigned int)scope; -#ifdef HAVE_IF_NAMETOINDEX - else { - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; - scopeidx = if_nametoindex(zoneid); - if(!scopeidx) { -#ifdef CURLVERBOSE - char buffer[STRERROR_LEN]; - infof(data, "Invalid zoneid: %s; %s", zoneid, - curlx_strerror(errno, buffer, sizeof(buffer))); -#endif - } - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX */ + curlx_safefree(data->state.up.scheme); + uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); + if(uc) + return Curl_uc_to_curlcode(uc); + Curl_bufref_free(&data->state.url); + /* after update, get the updated version */ + uc = curl_url_get(uh, CURLUPART_URL, &url, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + Curl_bufref_set(&data->state.url, url, 0, curl_free); - curlx_free(zoneid); + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &data->state.origin); + if(result) + return result; + infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); } + return CURLE_OK; } #else -#define zonefrom_url(a, b, c) Curl_nop_stmt +#define hsts_upgrade(x, y, z, a) CURLE_OK #endif +#ifndef CURL_DISABLE_NETRC +static bool str_has_ctrl(const char *input) +{ + if(input) { + const unsigned char *str = (const unsigned char *)input; + while(*str) { + if(*str < 0x20) + return TRUE; + str++; + } + } + return FALSE; +} + /* - * Parse URL and fill in the relevant members of the connection struct. + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. */ -static CURLcode parseurlandfillconn(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, + struct Curl_creds **pcreds) { - CURLcode result; - CURLU *uh; - CURLUcode uc; - char *hostname; - size_t hlen; - bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); - - up_free(data); /* cleanup previous leftovers first */ + struct Curl_creds *ncreds_out = NULL; + CURLcode result = CURLE_OK; - /* parse the URL */ - if(use_set_uh) { - uh = data->state.uh = curl_url_dup(data->set.uh); - } - else { - uh = data->state.uh = curl_url(); - } + if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ + struct Curl_creds *ncreds_in = NULL; + bool scan_netrc = TRUE; + NETRCcode ret; + CURLUcode uc; - if(!uh) - return CURLE_OUT_OF_MEMORY; + if(*pcreds) { + switch((*pcreds)->source) { + case CREDS_OPTION: + /* we never override credentials set via CURLOPT_*, leave. */ + scan_netrc = FALSE; + break; + case CREDS_URL: /* only apply when netrc is not required */ + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + /* We ignore password from URL */ + ncreds_in = *pcreds; + } + else if(!Curl_creds_has_user(*pcreds) || + !Curl_creds_has_passwd(*pcreds)) { + /* We use netrc to complete what is missing */ + ncreds_in = *pcreds; + } + else + scan_netrc = FALSE; + break; + default: /* ignore credentials from other sources */ + break; + } + } - if(data->set.str[STRING_DEFAULT_PROTOCOL] && - !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { - char *url = curl_maprintf("%s://%s", - data->set.str[STRING_DEFAULT_PROTOCOL], - Curl_bufref_ptr(&data->state.url)); - if(!url) - return CURLE_OUT_OF_MEMORY; - Curl_bufref_set(&data->state.url, url, 0, curl_free); - } + if(!scan_netrc) + goto out; - if(!use_set_uh) { - char *newurl; - uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url), - (unsigned int)(CURLU_GUESS_SCHEME | - CURLU_NON_SUPPORT_SCHEME | - (data->set.disallow_username_in_url ? - CURLU_DISALLOW_USER : 0) | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); - if(uc) { - failf(data, "URL rejected: %s", curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); + ret = Curl_netrc_scan(data, &data->state.netrc, + data->state.origin->hostname, + Curl_creds_user(ncreds_in), + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - - /* after it was parsed, get the generated normalized version */ - uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - Curl_bufref_set(&data->state.url, newurl, 0, curl_free); + else if(ret && ((ret == NETRC_NO_MATCH) || + (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { + infof(data, "Could not find host %s in the %s file; using defaults", + data->state.origin->hostname, + (data->set.str[STRING_NETRC_FILE] ? + data->set.str[STRING_NETRC_FILE] : ".netrc")); + } + else if(ret) { + const char *m = Curl_netrc_strerror(ret); + failf(data, ".netrc error: %s", m); + result = CURLE_READ_ERROR; + goto out; + } + else if(ncreds_out) { + if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) { + /* if the protocol cannot handle control codes in credentials, make + sure there are none */ + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { + failf(data, "control code detected in .netrc credentials"); + result = CURLE_READ_ERROR; + goto out; + } + } + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + data->state.origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + *pcreds, CREDS_NETRC, pcreds); + if(result) + goto out; + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(*pcreds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(*pcreds), + CURLU_URLENCODE); + if(uc) + result = Curl_uc_to_curlcode(uc); + } + else + DEBUGASSERT(0); } - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); - if(uc) { - if(!curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { - failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); - return CURLE_URL_MALFORMAT; - } +#ifdef CURLVERBOSE + Curl_creds_trace(data, data->state.creds, "transfer credentials"); +#endif - hostname = data->state.up.hostname; - hlen = hostname ? strlen(hostname) : 0; +out: + Curl_creds_unlink(&ncreds_out); + return result; +} +#endif /* CURL_DISABLE_NETRC */ - if(hostname && hostname[0] == '[') { - /* This looks like an IPv6 address literal. See if there is an address - scope. */ - /* cut off the brackets after copying this! */ - hostname++; - hlen -= 2; +static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) +{ + struct Curl_creds *newcreds = NULL; + CURLcode result = CURLE_OK; - zonefrom_url(uh, data, conn); + if((data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD] || + data->set.str[STRING_BEARER] || + data->set.str[STRING_SASL_AUTHZID] || + data->set.str[STRING_SERVICE_NAME]) && + Curl_auth_allowed_to_origin(data, data->state.origin)) { + result = Curl_creds_create(data->set.str[STRING_USERNAME], + data->set.str[STRING_PASSWORD], + data->set.str[STRING_BEARER], + data->set.str[STRING_SASL_AUTHZID], + data->set.str[STRING_SERVICE_NAME], + CREDS_OPTION, &newcreds); + if(result) + goto out; } - /* make sure the connect struct gets its own copy of the hostname */ - conn->host.rawalloc = curlx_strdup(hostname ? hostname : ""); - if(!conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */ - conn->host.name = conn->host.rawalloc; - - /************************************************************* - * IDN-convert the hostnames - *************************************************************/ - result = Curl_idnconvert_hostname(&conn->host); - if(result) - return result; + /* Extract credentials from the URL only if there are none OR + * if no CURLOPT_USER was set. */ + if(!newcreds || !Curl_creds_has_user(newcreds)) { + char *udecoded = NULL; + char *pdecoded = NULL; + CURLUcode uc; -#ifndef CURL_DISABLE_HSTS - /* HSTS upgrade */ - if(data->hsts && curl_strequal("http", data->state.up.scheme)) { - /* This MUST use the IDN decoded name */ - if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { - char *url; - curlx_safefree(data->state.up.scheme); - uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); - if(uc) - return Curl_uc_to_curlcode(uc); - Curl_bufref_free(&data->state.url); - /* after update, get the updated version */ - uc = curl_url_get(uh, CURLUPART_URL, &url, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) { - curlx_free(url); - return Curl_uc_to_curlcode(uc); - } - Curl_bufref_set(&data->state.url, url, 0, curl_free); - infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(uc && (uc != CURLUE_NO_USER)) + result = Curl_uc_to_curlcode(uc); + if(!result) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(uc && (uc != CURLUE_NO_PASSWORD)) + result = Curl_uc_to_curlcode(uc); + } + if(!result && data->state.up.user) { + result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, + (data->state.origin->scheme->flags & + PROTOPT_USERPWDCTRL) ? + REJECT_ZERO : REJECT_CTRL); } - } -#endif - - result = findprotocol(data, conn, data->state.up.scheme); - if(result) - return result; - - /* - * username and password set with their own options override the credentials - * possibly set in the URL, but netrc does not. - */ - if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? + if(!result && data->state.up.password) { + result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, + (data->state.origin->scheme->flags & + PROTOPT_USERPWDCTRL) ? REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->passwd = decoded; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; - data->state.creds_from = CREDS_URL; } - else if(uc != CURLUE_NO_PASSWORD) - return Curl_uc_to_curlcode(uc); - } + if(!result) + result = Curl_creds_merge(udecoded, pdecoded, newcreds, + CREDS_URL, &newcreds); - if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { - /* we do not use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->user = decoded; - result = Curl_setstropt(&data->state.aptr.user, decoded); - data->state.creds_from = CREDS_URL; + curlx_free(udecoded); + curlx_free(pdecoded); + if(result) { + failf(data, "error extracting credentials from URL"); + goto out; } - else if(uc != CURLUE_NO_USER) - return Curl_uc_to_curlcode(uc); - if(result) - return result; } - uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, - CURLU_URLDECODE); - if(!uc) { - conn->options = curlx_strdup(data->state.up.options); - if(!conn->options) - return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_NETRC + /* Check for overridden login details and set them accordingly so that + they are known when protocol->setup_connection is called! */ + result = url_set_data_creds_netrc(data, &newcreds); +#endif /* CURL_DISABLE_NETRC */ + +out: + if(!result && !Curl_creds_equal(data->state.creds, newcreds)) { + /* Do we have more things to trigger on credentials change? */ + Curl_creds_link(&data->state.creds, newcreds); } - else if(uc != CURLUE_NO_OPTIONS) - return Curl_uc_to_curlcode(uc); + Curl_creds_unlink(&newcreds); + return result; +} - uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); +static CURLcode url_set_conn_origin_etc(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, - CURLU_DEFAULT_PORT); - if(uc) { - if((uc == CURLUE_OUT_OF_MEMORY) || - !curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; + Curl_peer_link(&conn->origin, data->state.origin); + + /* set the connection scheme */ + result = url_set_conn_scheme(data, conn, conn->origin->scheme); + if(result) + goto out; + + /* set the connection options */ + if(data->set.str[STRING_OPTIONS]) { + conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]); + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } - else { - curl_off_t port; - bool valid = TRUE; - if(data->set.use_port && data->state.allow_port) - port = data->set.use_port; - else { - const char *p = data->state.up.port; - if(curlx_str_number(&p, &port, 0xffff)) - valid = FALSE; + else if(data->state.up.options) { + conn->options = curlx_strdup(data->state.up.options); + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - if(valid) - conn->remote_port = (unsigned short)port; } - uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); - if(uc && (uc != CURLUE_NO_QUERY)) - return CURLE_OUT_OF_MEMORY; - #ifdef USE_IPV6 - if(data->set.scope_id) - /* Override any scope that was set above. */ - conn->scope_id = data->set.scope_id; + conn->scope_id = data->set.scope_id ? + data->set.scope_id : data->state.origin->scopeid; #endif - return CURLE_OK; +out: + return result; } /* @@ -1784,42 +1673,31 @@ static CURLcode setup_range(struct Curl_easy *data) static CURLcode setup_connection_internals(struct Curl_easy *data, struct connectdata *conn) { - const char *hostname; - uint16_t port; + struct Curl_peer *peer = NULL; CURLcode result; - DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); if(conn->scheme->run->setup_connection) { result = conn->scheme->run->setup_connection(data, conn); if(result) return result; } - DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); /* Now create the destination name */ -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - hostname = conn->http_proxy.host.name; - port = conn->http_proxy.port; - } - else -#endif - { - port = conn->bits.conn_to_port ? - conn->conn_to_port : conn->remote_port; - hostname = conn->bits.conn_to_host ? - conn->conn_to_host.name : conn->host.name; - } + peer = Curl_conn_get_destination(conn, FIRSTSOCKET); + if(!peer) + return CURLE_FAILED_INIT; -#ifdef USE_IPV6 /* IPv6 addresses with a scope_id (0 is default == global) have a * printable representation with a '%' suffix. */ - if(conn->scope_id) - conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port, - conn->scope_id); + if(peer->ipv6) + if(peer->scopeid) + conn->destination = curl_maprintf("[%s%%%u]:%u", + peer->hostname, peer->scopeid, peer->port); + else + conn->destination = curl_maprintf("[%s]:%u", + peer->hostname, peer->port); else -#endif - conn->destination = curl_maprintf("%s:%u", hostname, port); + conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -1829,500 +1707,6 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, return CURLE_OK; } -#ifndef CURL_DISABLE_PROXY - -#ifndef CURL_DISABLE_HTTP -/**************************************************************** - * Detect what (if any) proxy to use. Remember that this selects a host - * name and is not limited to HTTP proxies only. - * The returned pointer must be freed by the caller (unless NULL) - ****************************************************************/ -static char *detect_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - - /* If proxy was not specified, we check for default proxy environment - * variables, to enable i.e Lynx compliance: - * - * http_proxy=http://some.server.dom:port/ - * https_proxy=http://some.server.dom:port/ - * ftp_proxy=http://some.server.dom:port/ - * no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - * all_proxy=http://some.server.dom:port/ - * (seems to exist for the CERN www lib. Probably - * the first to check for.) - * - * For compatibility, the all-uppercase versions of these variables are - * checked if the lowercase versions do not exist. - */ - char proxy_env[20]; - const char *envp; - VERBOSE(envp = proxy_env); - - curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", - conn->scheme->name); - - /* read the protocol proxy: */ - proxy = curl_getenv(proxy_env); - - /* - * We do not try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!proxy && !curl_strequal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - proxy = curl_getenv(proxy_env); - } - - if(!proxy) { -#ifndef CURL_DISABLE_WEBSOCKETS - /* websocket proxy fallbacks */ - if(curl_strequal("ws_proxy", proxy_env)) { - proxy = curl_getenv("http_proxy"); - } - else if(curl_strequal("wss_proxy", proxy_env)) { - proxy = curl_getenv("https_proxy"); - if(!proxy) - proxy = curl_getenv("HTTPS_PROXY"); - } - if(!proxy) { -#endif - envp = "all_proxy"; - proxy = curl_getenv(envp); /* default proxy to use */ - if(!proxy) { - envp = "ALL_PROXY"; - proxy = curl_getenv(envp); - } -#ifndef CURL_DISABLE_WEBSOCKETS - } -#endif - } - if(proxy) - infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); - - return proxy; -} -#endif /* CURL_DISABLE_HTTP */ - -/* - * If this is supposed to use a proxy, we need to figure out the proxy - * hostname, so that we can reuse an existing connection - * that may exist registered to the same proxy host. - */ -static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, const char *proxy, - long proxytype) -{ - char *portptr = NULL; - char *proxyuser = NULL; - char *proxypasswd = NULL; - char *host = NULL; - bool sockstype; - CURLUcode uc; - struct proxy_info *proxyinfo; - CURLU *uhp = curl_url(); - CURLcode result = CURLE_OK; - char *scheme = NULL; -#ifdef USE_UNIX_SOCKETS - char *path = NULL; - bool is_unix_proxy = FALSE; -#endif - - if(!uhp) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* When parsing the proxy, allowing non-supported schemes since we have - these made up ones for proxies. Guess scheme for URLs without it. */ - uc = curl_url_set(uhp, CURLUPART_URL, proxy, - CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); - if(!uc) { - /* parsed okay as a URL */ - uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(curl_strequal("https", scheme)) { - if(proxytype != CURLPROXY_HTTPS2) - proxytype = CURLPROXY_HTTPS; - else - proxytype = CURLPROXY_HTTPS2; - } - else if(curl_strequal("socks5h", scheme)) - proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(curl_strequal("socks5", scheme)) - proxytype = CURLPROXY_SOCKS5; - else if(curl_strequal("socks4a", scheme)) - proxytype = CURLPROXY_SOCKS4A; - else if(curl_strequal("socks4", scheme) || - curl_strequal("socks", scheme)) - proxytype = CURLPROXY_SOCKS4; - else if(curl_strequal("http", scheme)) - ; /* leave it as HTTP or HTTP/1.0 */ - else { - /* Any other xxx:// reject! */ - failf(data, "Unsupported proxy scheme for \'%s\'", proxy); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - else { - failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, - curl_url_strerror(uc)); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - - if(IS_HTTPS_PROXY(proxytype) && - !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; - goto error; - } - - sockstype = - proxytype == CURLPROXY_SOCKS5_HOSTNAME || - proxytype == CURLPROXY_SOCKS5 || - proxytype == CURLPROXY_SOCKS4A || - proxytype == CURLPROXY_SOCKS4; - - proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; - proxyinfo->proxytype = (unsigned char)proxytype; - - /* Is there a username and password given in this proxy URL? */ - uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_USER)) { - result = Curl_uc_to_curlcode(uc); - goto error; - } - uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_PASSWORD)) { - result = Curl_uc_to_curlcode(uc); - goto error; - } - - if(proxyuser || proxypasswd) { - curlx_free(proxyinfo->user); - proxyinfo->user = proxyuser; - result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); - proxyuser = NULL; - if(result) - goto error; - curlx_safefree(proxyinfo->passwd); - if(!proxypasswd) { - proxypasswd = curlx_strdup(""); - if(!proxypasswd) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - proxyinfo->passwd = proxypasswd; - result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); - proxypasswd = NULL; - if(result) - goto error; - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - } - - uc = curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); - if(uc == CURLUE_OUT_OF_MEMORY) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(portptr) { - curl_off_t num; - const char *p = portptr; - if(!curlx_str_number(&p, &num, UINT16_MAX)) - proxyinfo->port = (uint16_t)num; - /* Should we not error out when the port number is invalid? */ - curlx_free(portptr); - } - else { - if(data->set.proxyport) - /* None given in the proxy string, then get the default one if it is - given */ - proxyinfo->port = data->set.proxyport; - else { - if(IS_HTTPS_PROXY(proxytype)) - proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT; - else - proxyinfo->port = CURL_DEFAULT_PROXY_PORT; - } - } - - /* now, clone the proxy hostname */ - uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } -#ifdef USE_UNIX_SOCKETS - if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) { - uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* path will be "/", if no path was found */ - if(strcmp("/", path)) { - is_unix_proxy = TRUE; - curlx_free(host); - host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path); - if(!host) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - curlx_free(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - proxyinfo->host.name = host; - host = NULL; - } - } - - if(!is_unix_proxy) { -#endif - curlx_free(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - if(host[0] == '[') { - /* this is a numerical IPv6, strip off the brackets */ - size_t len = strlen(host); - host[len - 1] = 0; /* clear the trailing bracket */ - host++; - zonefrom_url(uhp, data, conn); - } - proxyinfo->host.name = host; - host = NULL; -#ifdef USE_UNIX_SOCKETS - } -#endif - -error: - curlx_free(proxyuser); - curlx_free(proxypasswd); - curlx_free(host); - curlx_free(scheme); -#ifdef USE_UNIX_SOCKETS - curlx_free(path); -#endif - curl_url_cleanup(uhp); - return result; -} - -/* - * Extract the user and password from the authentication string - */ -static CURLcode parse_proxy_auth(struct Curl_easy *data, - struct connectdata *conn) -{ - const char *proxyuser = data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : ""; - const char *proxypasswd = data->state.aptr.proxypasswd ? - data->state.aptr.proxypasswd : ""; - CURLcode result = CURLE_OUT_OF_MEMORY; - - conn->http_proxy.user = curlx_strdup(proxyuser); - if(conn->http_proxy.user) { - conn->http_proxy.passwd = curlx_strdup(proxypasswd); - if(conn->http_proxy.passwd) - result = CURLE_OK; - else - curlx_safefree(conn->http_proxy.user); - } - return result; -} - -/* create_conn helper to parse and init proxy values. to be called after Unix - socket init but before any proxy vars are evaluated. */ -static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - char *socksproxy = NULL; - char *no_proxy = NULL; - CURLcode result = CURLE_OK; - - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result) - goto out; - } - - /************************************************************* - * Detect what (if any) proxy to use - *************************************************************/ - if(data->set.str[STRING_PROXY]) { - proxy = curlx_strdup(data->set.str[STRING_PROXY]); - /* if global proxy is set, this is it */ - if(!proxy) { - failf(data, "memory shortage"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_PRE_PROXY]) { - socksproxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); - /* if global socks proxy is set, this is it */ - if(!socksproxy) { - failf(data, "memory shortage"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(!data->set.str[STRING_NOPROXY]) { - const char *p = "no_proxy"; - no_proxy = curl_getenv(p); - if(!no_proxy) { - p = "NO_PROXY"; - no_proxy = curl_getenv(p); - } - if(no_proxy) { - infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy); - } - } - - if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy)) { - curlx_safefree(proxy); - curlx_safefree(socksproxy); - } -#ifndef CURL_DISABLE_HTTP - else if(!proxy && !socksproxy) - /* if the host is not in the noproxy list, detect proxy. */ - proxy = detect_proxy(data, conn); -#endif /* CURL_DISABLE_HTTP */ - curlx_safefree(no_proxy); - -#ifdef USE_UNIX_SOCKETS - /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ - if(proxy && conn->unix_domain_socket) { - curlx_free(proxy); - proxy = NULL; - } -#endif - - if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(proxy); /* Do not bother with an empty proxy string - or if the protocol does not work with network */ - proxy = NULL; - } - if(socksproxy && (!*socksproxy || - (conn->scheme->flags & PROTOPT_NONETWORK))) { - curlx_free(socksproxy); /* Do not bother with an empty socks proxy string - or if the protocol does not work with - network */ - socksproxy = NULL; - } - - /*********************************************************************** - * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can reuse an existing - * connection that may exist registered to the same proxy host. - ***********************************************************************/ - if(proxy || socksproxy) { - if(proxy) { - result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); - curlx_safefree(proxy); /* parse_proxy copies the proxy string */ - if(result) - goto out; - } - - if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, - conn->socks_proxy.proxytype); - /* parse_proxy copies the socks proxy string */ - curlx_safefree(socksproxy); - if(result) - goto out; - } - - if(conn->http_proxy.host.rawalloc) { -#ifdef CURL_DISABLE_HTTP - /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; -#else - /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { - if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && - !conn->bits.tunnel_proxy) - conn->scheme = &Curl_scheme_http; - else - /* if not converting to HTTP over the proxy, enforce tunneling */ - conn->bits.tunnel_proxy = TRUE; - } - conn->bits.httpproxy = TRUE; -#endif - } - else { - conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ - conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ - } - - if(conn->socks_proxy.host.rawalloc) { - if(!conn->http_proxy.host.rawalloc) { - /* once a socks proxy */ - if(!conn->socks_proxy.user) { - conn->socks_proxy.user = conn->http_proxy.user; - conn->http_proxy.user = NULL; - curlx_free(conn->socks_proxy.passwd); - conn->socks_proxy.passwd = conn->http_proxy.passwd; - conn->http_proxy.passwd = NULL; - } - } - conn->bits.socksproxy = TRUE; - } - else - conn->bits.socksproxy = FALSE; /* not a socks proxy */ - } - else { - conn->bits.socksproxy = FALSE; - conn->bits.httpproxy = FALSE; - } - conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; - - if(!conn->bits.proxy) { - /* we are not using the proxy after all... */ - conn->bits.proxy = FALSE; - conn->bits.httpproxy = FALSE; - conn->bits.socksproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; - conn->bits.tunnel_proxy = FALSE; - /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need - to signal that CURLPROXY_HTTPS is not used for this connection */ - conn->http_proxy.proxytype = CURLPROXY_HTTP; - } - -out: - - curlx_free(socksproxy); - curlx_free(proxy); - return result; -} -#endif /* CURL_DISABLE_PROXY */ - /* * Curl_parse_login_details() * @@ -2411,309 +1795,35 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, *userp = ubuf; *passwdp = pbuf; return CURLE_OK; -error: - curlx_free(ubuf); - curlx_free(pbuf); - return CURLE_OUT_OF_MEMORY; -} - -/************************************************************* - * Figure out the remote port number and fix it in the URL - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * The port number embedded in the URL is replaced, if necessary. - *************************************************************/ -static CURLcode parse_remote_port(struct Curl_easy *data, - struct connectdata *conn) -{ - if(data->set.use_port && data->state.allow_port) { - /* if set, we use this instead of the port possibly given in the URL */ - char portbuf[16]; - CURLUcode uc; - conn->remote_port = data->set.use_port; - curl_msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); - uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); - if(uc) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -#ifndef CURL_DISABLE_NETRC -static bool str_has_ctrl(const char *input) -{ - if(input) { - const unsigned char *str = (const unsigned char *)input; - while(*str) { - if(*str < 0x20) - return TRUE; - str++; - } - } - return FALSE; -} -#endif - -/* - * Override the login details from the URL with that in the CURLOPT_USERPWD - * option or a .netrc file, if applicable. - */ -static CURLcode override_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLUcode uc; - char **userp = &conn->user; - char **passwdp = &conn->passwd; - char **optionsp = &conn->options; - - if(data->set.str[STRING_OPTIONS]) { - curlx_free(*optionsp); - *optionsp = curlx_strdup(data->set.str[STRING_OPTIONS]); - if(!*optionsp) - return CURLE_OUT_OF_MEMORY; - } - -#ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - curlx_safefree(*userp); - curlx_safefree(*passwdp); - } - conn->bits.netrc = FALSE; - if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { - bool url_provided = FALSE; - - if(data->state.aptr.user && - (data->state.creds_from != CREDS_NETRC)) { - /* there was a username with a length in the URL. Use the URL decoded - version */ - userp = &data->state.aptr.user; - url_provided = TRUE; - } - - if(!*passwdp) { - NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - if(ret == NETRC_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; - else if(ret && ((ret == NETRC_NO_MATCH) || - (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { - infof(data, "Could not find host %s in the %s file; using defaults", - conn->host.name, - (data->set.str[STRING_NETRC_FILE] ? - data->set.str[STRING_NETRC_FILE] : ".netrc")); - } - else if(ret) { - const char *m = Curl_netrc_strerror(ret); - failf(data, ".netrc error: %s", m); - return CURLE_READ_ERROR; - } - else { - if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { - /* if the protocol cannot handle control codes in credentials, make - sure there are none */ - if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) { - failf(data, "control code detected in .netrc credentials"); - return CURLE_READ_ERROR; - } - } - /* set bits.netrc TRUE to remember that we got the name from a .netrc - file, so that it is safe to use even if we followed a Location: to a - different host or similar. */ - conn->bits.netrc = TRUE; - } - } - if(url_provided) { - curlx_free(conn->user); - conn->user = curlx_strdup(*userp); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - /* no user was set but a password, set a blank user */ - if(!*userp && *passwdp) { - *userp = curlx_strdup(""); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } -#endif - - /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result; - if(data->state.aptr.user != *userp) { - /* nothing to do then */ - result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = curlx_strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } - if(*passwdp) { - CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - if(data->state.aptr.passwd) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - data->state.aptr.passwd, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*passwdp) { - *passwdp = curlx_strdup(data->state.aptr.passwd); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; - } - } - - return CURLE_OK; -} - -/* - * Set the login details so they are available in the connection - */ -static CURLcode set_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - const char *setuser = CURL_DEFAULT_USER; - const char *setpasswd = CURL_DEFAULT_PASSWORD; - - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) - ; - else { - setuser = ""; - setpasswd = ""; - } - /* Store the default user */ - if(!conn->user) { - conn->user = curlx_strdup(setuser); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - - /* Store the default password */ - if(!conn->passwd) { - conn->passwd = curlx_strdup(setpasswd); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - - return result; +error: + curlx_free(ubuf); + curlx_free(pbuf); + return CURLE_OUT_OF_MEMORY; } /* - * Parses a "host:port" string to connect to. - * The hostname and the port may be empty; in this case, NULL is returned for - * the hostname and -1 for the port. + * Set the login details so they are available in the connection */ -static CURLcode parse_connect_to_host_port(struct Curl_easy *data, - const char *host, - char **hostname_result, - int *port_result) +static CURLcode url_set_conn_login(struct Curl_easy *data, + struct connectdata *conn) { - char *host_dup; - char *hostptr; - char *host_portno; - char *portptr; - int port = -1; - CURLcode result = CURLE_OK; - - *hostname_result = NULL; - *port_result = -1; - - if(!host || !*host) - return CURLE_OK; - - host_dup = curlx_strdup(host); - if(!host_dup) - return CURLE_OUT_OF_MEMORY; - - hostptr = host_dup; - - /* start scanning for port number at this point */ - portptr = hostptr; - - /* detect and extract RFC6874-style IPv6-addresses */ - if(*hostptr == '[') { -#ifdef USE_IPV6 - char *ptr = ++hostptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) - ptr++; - if(*ptr == '%') { - /* There might be a zone identifier */ - if(strncmp("%25", ptr, 3)) - infof(data, "Please URL encode %% as %%25, see RFC 6874."); - ptr++; - /* Allow unreserved characters as defined in RFC 3986 */ - while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || - (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) - ptr++; - } - if(*ptr == ']') - /* yeps, it ended nicely with a bracket as well */ - *ptr++ = '\0'; + /* If our protocol needs a password and we have none, use the defaults */ + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) { + Curl_peer_link(&conn->creds_origin, data->state.origin); + if(data->state.creds) + Curl_creds_link(&conn->creds, data->state.creds); else - infof(data, "Invalid IPv6 address format"); - portptr = ptr; - /* Note that if this did not end with a bracket, we still advanced the - * hostptr first, but I cannot see anything wrong with that as no host - * name nor a numeric can legally start with a bracket. - */ -#else - failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); - result = CURLE_NOT_BUILT_IN; - goto error; -#endif - } - - /* Get port number off server.com:1080 */ - host_portno = strchr(portptr, ':'); - if(host_portno) { - *host_portno = '\0'; /* cut off number from hostname */ - host_portno++; - if(*host_portno) { - curl_off_t portparse; - const char *p = host_portno; - if(curlx_str_number(&p, &portparse, 0xffff)) { - failf(data, "No valid port number in connect to host string (%s)", - host_portno); - result = CURLE_SETOPT_OPTION_SYNTAX; - goto error; - } - port = (int)portparse; /* we know it will fit */ - } + return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD, + NULL, NULL, NULL, CREDS_NONE, &conn->creds); } - - /* now, clone the cleaned hostname */ - DEBUGASSERT(hostptr); - *hostname_result = curlx_strdup(hostptr); - if(!*hostname_result) { - result = CURLE_OUT_OF_MEMORY; - goto error; + else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { + /* for protocols that do not handle credentials per request, + * the connection credentials are set by the initial transfer. */ + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, data->state.creds); } - *port_result = port; - -error: - curlx_free(host_dup); - return result; + return CURLE_OK; } /* @@ -2721,18 +1831,16 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". */ static CURLcode parse_connect_to_string(struct Curl_easy *data, - struct connectdata *conn, - const char *conn_to_host, - char **host_result, - int *port_result) + const struct Curl_peer *dest, + const char *conn_to_line, + struct Curl_peer **pvia_dest) { CURLcode result = CURLE_OK; - const char *ptr = conn_to_host; + const char *ptr = conn_to_line; bool host_match = FALSE; bool port_match = FALSE; - *host_result = NULL; - *port_result = -1; + *pvia_dest = NULL; if(*ptr == ':') { /* an empty hostname always matches */ @@ -2743,9 +1851,8 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, /* check whether the URL's hostname matches. Use the URL hostname * when it was an IPv6 address. Otherwise use the connection's hostname * that has IDN conversion. */ - char *hostname_to_match = - (data->state.up.hostname && data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name; + const char *hostname_to_match = (dest->user_hostname[0] == '[') ? + dest->user_hostname : dest->hostname; size_t hlen = strlen(hostname_to_match); host_match = curl_strnequal(ptr, hostname_to_match, hlen); ptr += hlen; @@ -2766,68 +1873,44 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, if(ptr_next) { curl_off_t port_to_match; if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && - (port_to_match == (curl_off_t)conn->remote_port)) + ((uint16_t)port_to_match == dest->port)) { port_match = TRUE; + } ptr = ptr_next + 1; } } } - if(host_match && port_match) { - /* parse the hostname and port to connect to */ - result = parse_connect_to_host_port(data, ptr, host_result, port_result); - } + if(host_match && port_match && ptr && *ptr) + result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest); return result; } -/* - * Processes all strings in the "connect to" slist, and uses the "connect - * to host" and "connect to port" of the first string that matches. - */ -static CURLcode parse_connect_to_slist(struct Curl_easy *data, - struct connectdata *conn, - struct curl_slist *conn_to_host) +/* With `conn->origin` known, determine if we should talk to that + * directly or via another peer. This is the result of inspecting + * the "connect to" slist and "alt-svc" settings. */ +static CURLcode url_set_conn_peer(struct Curl_easy *data, + struct connectdata *conn) { CURLcode result = CURLE_OK; - char *host = NULL; - int port = -1; + const struct Curl_peer *origin = conn->origin; + struct Curl_peer *via_peer = NULL; + struct curl_slist *conn_to_entry = data->set.connect_to; + + DEBUGASSERT(!conn->via_peer); + Curl_peer_unlink(&conn->via_peer); - while(conn_to_host && !host && port == -1) { - result = parse_connect_to_string(data, conn, conn_to_host->data, - &host, &port); + while(conn_to_entry && !via_peer) { + result = parse_connect_to_string(data, origin, conn_to_entry->data, + &via_peer); if(result) return result; - - if(host && *host) { - conn->conn_to_host.rawalloc = host; - conn->conn_to_host.name = host; - conn->bits.conn_to_host = TRUE; - - infof(data, "Connecting to hostname: %s", host); - } - else { - /* no "connect to host" */ - conn->bits.conn_to_host = FALSE; - curlx_safefree(host); - } - - if(port >= 0) { - conn->conn_to_port = (uint16_t)port; - conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %u", conn->conn_to_port); - } - else { - /* no "connect to port" */ - conn->bits.conn_to_port = FALSE; - port = -1; - } - - conn_to_host = conn_to_host->next; + conn_to_entry = conn_to_entry->next; } #ifndef CURL_DISABLE_ALTSVC - if(data->asi && !host && (port == -1) && + if(data->asi && !via_peer && ((conn->scheme->protocol == CURLPROTO_HTTPS) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ @@ -2858,13 +1941,13 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, allowed_alpns |= ALPN_h1; allowed_alpns &= (int)data->asi->flags; - host = conn->host.rawalloc; - DEBUGF(infof(data, "check Alt-Svc for host %s", host)); + DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname)); #ifdef USE_HTTP3 if(!hit && (neg->wanted & CURL_HTTP_V3x)) { srcalpnid = ALPN_h3; hit = Curl_altsvc_lookup(data->asi, - ALPN_h3, host, conn->remote_port, /* from */ + ALPN_h3, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2874,7 +1957,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->h2_prior_knowledge) { srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h2, host, conn->remote_port, /* from */ + ALPN_h2, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2883,7 +1967,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->only_10) { srcalpnid = ALPN_h1; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, host, conn->remote_port, /* from */ + ALPN_h1, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2908,18 +1993,16 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } } else if(hit) { - char *hostd = curlx_strdup(as->dst.host); - if(!hostd) - return CURLE_OUT_OF_MEMORY; - conn->conn_to_host.rawalloc = hostd; - conn->conn_to_host.name = hostd; - conn->bits.conn_to_host = TRUE; - conn->conn_to_port = as->dst.port; - conn->bits.conn_to_port = TRUE; - conn->bits.altused = TRUE; + result = Curl_peer_create(data, conn->origin->scheme, + as->dst.host, as->dst.port, + &via_peer); + if(result) + return result; infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", - Curl_alpnid2str(srcalpnid), host, conn->remote_port, - Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + Curl_alpnid2str(srcalpnid), origin->hostname, origin->port, + Curl_alpnid2str(as->dst.alpnid), + via_peer->hostname, via_peer->port); + conn->bits.altused = TRUE; if(srcalpnid != as->dst.alpnid) { /* protocol version switch */ switch(as->dst.alpnid) { @@ -2942,15 +2025,10 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } #endif - return result; -} + if(via_peer) + conn->via_peer = via_peer; -static void url_move_hostname(struct hostname *dest, struct hostname *src) -{ - curlx_safefree(dest->rawalloc); - Curl_free_idnconverted_hostname(dest); - *dest = *src; - memset(src, 0, sizeof(*src)); + return result; } /* @@ -2963,51 +2041,32 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, /* get the user+password information from the needle since it may * be new for this request even when we reuse conn */ - if(needle->user) { + if(needle->creds) { /* use the new username and password though */ - curlx_free(conn->user); - curlx_free(conn->passwd); - conn->user = needle->user; - conn->passwd = needle->passwd; - needle->user = NULL; - needle->passwd = NULL; + Curl_creds_link(&conn->creds, needle->creds); } #ifndef CURL_DISABLE_PROXY - conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy username and proxy password though */ - curlx_free(conn->http_proxy.user); - curlx_free(conn->socks_proxy.user); - curlx_free(conn->http_proxy.passwd); - curlx_free(conn->socks_proxy.passwd); - conn->http_proxy.user = needle->http_proxy.user; - conn->socks_proxy.user = needle->socks_proxy.user; - conn->http_proxy.passwd = needle->http_proxy.passwd; - conn->socks_proxy.passwd = needle->socks_proxy.passwd; - needle->http_proxy.user = NULL; - needle->socks_proxy.user = NULL; - needle->http_proxy.passwd = NULL; - needle->socks_proxy.passwd = NULL; - } + /* use the new proxy username and proxy password though */ + Curl_creds_link(&conn->http_proxy.creds, needle->http_proxy.creds); + Curl_creds_link(&conn->socks_proxy.creds, needle->socks_proxy.creds); #endif /* Finding a connection for reuse in the cpool matches, among other * things on the "remote-relevant" hostname. This is not necessarily - * the authority of the URL, e.g. conn->host. For example: + * the authority of the URL, e.g. conn->origin. For example: * - we use a proxy (not tunneling). we want to send all requests * that use the same proxy on this connection. * - we have a "connect-to" setting that may redirect the hostname of * a new request to the same remote endpoint of an existing conn. * We want to reuse an existing conn to the remote endpoint. - * Since connection reuse does not match on conn->host necessarily, we + * Since connection reuse does not match on conn->origin necessarily, we * switch conn to needle's host settings. */ - url_move_hostname(&conn->host, &needle->host); - url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - - conn->conn_to_port = needle->conn_to_port; - conn->remote_port = needle->remote_port; + Curl_peer_link(&conn->origin, needle->origin); + Curl_peer_link(&conn->via_peer, needle->via_peer); + Curl_peer_link(&conn->origin2, needle->origin2); + Curl_peer_link(&conn->via_peer2, needle->via_peer2); } static void conn_meta_freeentry(void *p) @@ -3016,7 +2075,7 @@ static void conn_meta_freeentry(void *p) /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ - DEBUGASSERT(p == NULL); + DEBUGASSERT(!p); } static CURLcode url_create_needle(struct Curl_easy *data, @@ -3024,14 +2083,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, { struct connectdata *needle = NULL; CURLcode result = CURLE_OK; - - /************************************************************* - * Check input data - *************************************************************/ - if(!Curl_bufref_ptr(&data->state.url)) { - result = CURLE_URL_MALFORMAT; - goto out; - } + bool network_scheme = TRUE; /* almost all are */ /* First, split up the current URL in parts so that we can use the parts for checking against the already present connections. In order @@ -3047,126 +2099,64 @@ static CURLcode url_create_needle(struct Curl_easy *data, Curl_hash_init(&needle->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); - result = parseurlandfillconn(data, needle); - if(result) - goto out; - - if(data->set.str[STRING_SASL_AUTHZID]) { - needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!needle->sasl_authzid) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_BEARER]) { - needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!needle->oauth_bearer) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - -#ifdef USE_UNIX_SOCKETS - if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - needle->unix_domain_socket = - curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!needle->unix_domain_socket) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - needle->bits.abstract_unix_socket = data->set.abstract_unix_socket; - } -#endif - - /* After the Unix socket init but before the proxy vars are used, parse and - initialize the proxy vars */ -#ifndef CURL_DISABLE_PROXY - result = create_conn_helper_init_proxy(data, needle); - if(result) - goto out; - - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) - needle->bits.tunnel_proxy = TRUE; -#endif - /************************************************************* - * Figure out the remote port number and fix it in the URL + * Determine `conn->origin` and populate `data->state.up` and + * other URL related properties. *************************************************************/ - result = parse_remote_port(data, needle); + result = url_set_conn_origin_etc(data, needle); if(result) goto out; - /* Check for overridden login details and set them accordingly so that - they are known when protocol->setup_connection is called! */ - result = override_login(data, needle); - if(result) - goto out; - - result = set_login(data, needle); /* default credentials */ - if(result) - goto out; - - /************************************************************* - * Process the "connect to" linked list of hostname/port mappings. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - result = parse_connect_to_slist(data, needle, data->set.connect_to); - if(result) - goto out; + DEBUGASSERT(needle->origin); + network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK); +#ifdef USE_UNIX_SOCKETS /************************************************************* - * IDN-convert the proxy hostnames + * Set UDS first. It overrides "via_peer" and proxy settings. *************************************************************/ -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - result = Curl_idnconvert_hostname(&needle->http_proxy.host); - if(result) - goto out; - } - if(needle->bits.socksproxy) { - result = Curl_idnconvert_hostname(&needle->socks_proxy.host); + if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) { + result = Curl_peer_uds_create(needle->origin->scheme, + data->set.str[STRING_UNIX_SOCKET_PATH], + (bool)data->set.abstract_unix_socket, + &needle->via_peer); if(result) goto out; } -#endif - if(needle->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&needle->conn_to_host); +#endif /* USE_UNIX_SOCKETS */ + + if(network_scheme && !needle->via_peer) { + /************************************************************* + * If the `via_peer` is not already set (via UDS above), + * determine if we talk to `conn->origin` directly or use + * `conn->via_peer` using "connect to" and "alt-svc" properties. + *************************************************************/ + result = url_set_conn_peer(data, needle); if(result) goto out; } /************************************************************* * Check whether the host and the "connect to host" are equal. - * Do this after the hostnames have been IDN-converted. + * Do this after the hostnames have been IDN-converted and + * before initializing the proxy. *************************************************************/ - if(needle->bits.conn_to_host && - curl_strequal(needle->conn_to_host.name, needle->host.name)) { - needle->bits.conn_to_host = FALSE; + if(Curl_peer_equal(needle->origin, needle->via_peer)) { + Curl_peer_unlink(&needle->via_peer); } - /************************************************************* - * Check whether the port and the "connect to port" are equal. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - if(needle->bits.conn_to_port && - needle->conn_to_port == needle->remote_port) { - needle->bits.conn_to_port = FALSE; +#ifndef CURL_DISABLE_PROXY + /* Going via a unix socket ignores any proxy settings */ + if(network_scheme && + (!needle->via_peer || !needle->via_peer->unix_socket)) { + result = Curl_proxy_init_conn(data, needle); + if(result) + goto out; } +#endif /* CURL_DISABLE_PROXY */ -#ifndef CURL_DISABLE_PROXY - /************************************************************* - * If the "connect to" feature is used with an HTTP proxy, - * we set the tunnel_proxy bit. - *************************************************************/ - if((needle->bits.conn_to_host || needle->bits.conn_to_port) && - needle->bits.httpproxy) - needle->bits.tunnel_proxy = TRUE; -#endif + result = url_set_conn_login(data, needle); /* default credentials */ + if(result) + goto out; /************************************************************* * Setup internals depending on protocol. Needs to be done after @@ -3183,7 +2173,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, needle->bits.tls_enable_alpn = TRUE; } - if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { + if(network_scheme) { /* Setup callbacks for network connections */ needle->recv[FIRSTSOCKET] = Curl_cf_recv; needle->send[FIRSTSOCKET] = Curl_cf_send; @@ -3191,7 +2181,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, needle->send[SECONDARYSOCKET] = Curl_cf_send; needle->bits.tcp_fastopen = data->set.tcp_fastopen; #ifdef USE_UNIX_SOCKETS - if(Curl_conn_get_unix_path(needle)) + if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket) needle->transport_wanted = TRNSPRT_UNIX; #endif } @@ -3199,6 +2189,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, out: if(!result) { DEBUGASSERT(needle); + DEBUGASSERT(needle->origin); *pneedle = needle; } else { @@ -3209,6 +2200,118 @@ static CURLcode url_create_needle(struct Curl_easy *data, return result; } +static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + CURLU *uh; + CURLUcode uc; + bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); + uint16_t port_override = data->state.allow_port ? data->set.use_port : 0; + uint32_t scope_id = 0; + + /************************************************************* + * Check input data + *************************************************************/ + if(!Curl_bufref_ptr(&data->state.url)) { + result = CURLE_URL_MALFORMAT; + goto out; + } + + up_free(data); /* cleanup previous leftovers first */ + + /* parse the URL */ + if(use_set_uh) + uh = data->state.uh = curl_url_dup(data->set.uh); + else + uh = data->state.uh = curl_url(); + if(!uh) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* Calculate the *real* URL this transfer uses, applying defaults + * where information is missing. */ + if(data->set.str[STRING_DEFAULT_PROTOCOL] && + !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { + char *url = curl_maprintf("%s://%s", + data->set.str[STRING_DEFAULT_PROTOCOL], + Curl_bufref_ptr(&data->state.url)); + if(!url) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_bufref_set(&data->state.url, url, 0, curl_free); + } + + if(!use_set_uh) { + char *newurl; + uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url), + (unsigned int)(CURLU_GUESS_SCHEME | + CURLU_NON_SUPPORT_SCHEME | + (data->set.disallow_username_in_url ? + CURLU_DISALLOW_USER : 0) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + failf(data, "URL rejected: %s", curl_url_strerror(uc)); + result = Curl_uc_to_curlcode(uc); + goto out; + } + + /* after it was parsed, get the generated normalized version */ + uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + Curl_bufref_set(&data->state.url, newurl, 0, curl_free); + } + +#ifdef USE_IPV6 + scope_id = data->set.scope_id; +#endif + + /* `uh` is now as the connection should use it, probably. */ + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &data->state.origin); + if(result) + goto out; + /* The origin might get changed when HSTS applies */ + result = hsts_upgrade(data, uh, port_override, scope_id); + if(result) + goto out; + + /* When the transfers initial_origin is not set, this is the initial + * request. Remember this starting point. */ + if(!data->state.initial_origin) + Curl_peer_link(&data->state.initial_origin, data->state.origin); + + uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, + CURLU_GET_EMPTY); + if(uc && (uc != CURLUE_NO_QUERY)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, + CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_OPTIONS)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + + result = url_set_data_creds(data, uh); + if(result) + goto out; + +out: + return result; +} + /** * Find an existing connection for the transfer or create a new one. * Returns @@ -3270,7 +2373,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) #endif /* Complete the easy's SSL configuration for connection cache matching */ - result = Curl_ssl_easy_config_complete(data); + result = Curl_ssl_easy_config_complete(data, needle->origin); if(result) goto out; @@ -3303,22 +2406,21 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) infof(data, "Reusing existing %s: connection%s with %s %s", conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", - conn->bits.proxy ? "proxy" : "host", - conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.name ? conn->http_proxy.host.dispname : - conn->host.dispname); + (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host", + conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : + conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : + conn->origin->hostname); #else infof(data, "Reusing existing %s: connection%s with host %s", conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", - conn->host.dispname); + conn->origin->hostname); #endif } else { /* We have decided that we want a new connection. We may not be able to do that if we have reached the limit of how many connections we are allowed to open. */ - DEBUGF(infof(data, "new connection, bits.close=%d", needle->bits.close)); if(waitpipe) { /* There is a connection that *might* become usable for multiplexing @@ -3353,7 +2455,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) * remaining parts like the cloned SSL configuration. */ result = Curl_ssl_conn_config_init(data, needle); if(result) { - DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n")); + DEBUGF(curl_mfprintf(stderr, "Error: init connection SSL config\n")); goto out; } /* attach it and no longer own it */ @@ -3402,7 +2504,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) #ifdef CURL_DISABLE_PROXY 0 #else - data->conn->bits.proxy + (data->conn->socks_proxy.peer || data->conn->http_proxy.peer) #endif ; @@ -3419,21 +2521,33 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) { CURLcode result; - struct connectdata *conn; + struct connectdata *conn = NULL; *pconnected = FALSE; /* Set the request to virgin state based on transfer settings */ Curl_req_hard_reset(&data->req, data); + /* Determine the origin of the transfer and what credentials to use */ + result = url_set_data_origin_and_creds(data); + if(result) + goto out; + if(!data->state.origin) { /* just make really sure */ + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; + } /* Get or create a connection for the transfer. */ result = url_find_or_create_conn(data); conn = data->conn; - if(result) goto out; + if(!data->conn) { /* just make really sure */ + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; + } - DEBUGASSERT(conn); Curl_pgrsTime(data, TIMER_POSTQUEUE); if(conn->bits.reuse) { if(conn->attached_xfers > 1) @@ -3445,11 +2559,10 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) *pconnected = TRUE; } else { - result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, - CURL_CF_SSL_DEFAULT); + result = Curl_conn_setup(data, conn, FIRSTSOCKET, CURL_CF_SSL_DEFAULT); if(!result) result = Curl_headers_init(data); - CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); + CURL_TRC_M(data, "Curl_conn_setup() -> %d", (int)result); } out: @@ -3505,96 +2618,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) #if defined(USE_HTTP2) || defined(USE_HTTP3) -#ifdef USE_NGHTTP2 - -static void priority_remove_child(struct Curl_easy *parent, - struct Curl_easy *child) -{ - struct Curl_data_prio_node **pnext = &parent->set.priority.children; - struct Curl_data_prio_node *pnode = parent->set.priority.children; - - DEBUGASSERT(child->set.priority.parent == parent); - while(pnode && pnode->data != child) { - pnext = &pnode->next; - pnode = pnode->next; - } - - DEBUGASSERT(pnode); - if(pnode) { - *pnext = pnode->next; - curlx_free(pnode); - } - - child->set.priority.parent = 0; - child->set.priority.exclusive = FALSE; -} - -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(child->set.priority.parent) { - priority_remove_child(child->set.priority.parent, child); - } - - if(parent) { - struct Curl_data_prio_node **tail; - struct Curl_data_prio_node *pnode; - - pnode = curlx_calloc(1, sizeof(*pnode)); - if(!pnode) - return CURLE_OUT_OF_MEMORY; - pnode->data = child; - - if(parent->set.priority.children && exclusive) { - /* exclusive: move all existing children underneath the new child */ - struct Curl_data_prio_node *node = parent->set.priority.children; - while(node) { - node->data->set.priority.parent = child; - node = node->next; - } - - tail = &child->set.priority.children; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.priority.children; - parent->set.priority.children = 0; - } - - tail = &parent->set.priority.children; - while(*tail) { - (*tail)->data->set.priority.exclusive = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = pnode; - } - - child->set.priority.parent = parent; - child->set.priority.exclusive = exclusive; - return CURLE_OK; -} - -#endif /* USE_NGHTTP2 */ - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data) -{ - while(data->set.priority.children) { - struct Curl_easy *tmp = data->set.priority.children->data; - priority_remove_child(data, tmp); - if(data->set.priority.parent) - Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); - } - - if(data->set.priority.parent) - priority_remove_child(data->set.priority.parent, data); -} -#endif - void Curl_data_priority_clear_state(struct Curl_easy *data) { memset(&data->state.priority, 0, sizeof(data->state.priority)); diff --git a/deps/curl/lib/url.h b/deps/curl/lib/url.h index 66baf70174..ebbe9d53c4 100644 --- a/deps/curl/lib/url.h +++ b/deps/curl/lib/url.h @@ -25,6 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" +/* Reject URLs exceeding this length */ +#define MAX_URL_LEN 0xffff + /* * Prototypes for library-wide functions */ diff --git a/deps/curl/lib/urlapi.c b/deps/curl/lib/urlapi.c index 103e0a6b57..05c79dfb0d 100644 --- a/deps/curl/lib/urlapi.c +++ b/deps/curl/lib/urlapi.c @@ -253,12 +253,18 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace) * Parse the login details (username, password and options) from the URL and * strip them out of the hostname * + * @unittest 1675 */ -static CURLUcode parse_hostname_login(struct Curl_URL *u, - const char *login, - size_t len, - unsigned int flags, - size_t *offset) /* to the hostname */ +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset); +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset) { CURLUcode ures = CURLUE_OK; CURLcode result; @@ -278,7 +284,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, DEBUGASSERT(login); - *offset = 0; + *hostname_offset = 0; ptr = memchr(login, '@', len); if(!ptr) goto out; @@ -326,7 +332,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, } /* the hostname starts at this offset */ - *offset = ptr - login; + *hostname_offset = ptr - login; return CURLUE_OK; out: @@ -334,9 +340,9 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, curlx_free(userp); curlx_free(passwdp); curlx_free(optionsp); - u->user = NULL; - u->password = NULL; - u->options = NULL; + curlx_safefree(u->user); + curlx_safefree(u->password); + curlx_safefree(u->options); return ures; } @@ -471,10 +477,17 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return ipv6_parse(u, hostname, hlen); else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%|"); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; + else if((hlen >= 2) && + (hostname[hlen - 1] == '.') && (hostname[hlen - 2] == '.')) + /* more than one trailing dot is not allowed */ + return CURLUE_BAD_HOSTNAME; + else if((hlen == 1) && (hostname[0] == '.')) + /* a single dot alone is not allowed */ + return CURLUE_BAD_HOSTNAME; } return CURLUE_OK; } @@ -489,11 +502,13 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * Output the "normalized" version of that input string in plain quad decimal * integers. * + * A single dot following the numerical address is accepted and "swallowed" as + * if it was never there. + * * Returns the host type. * * @unittest 1675 */ - UNITTEST int ipv4_normalize(struct dynbuf *host); UNITTEST int ipv4_normalize(struct dynbuf *host) { @@ -510,9 +525,11 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) int rc; curl_off_t l; if(*c == '0') { - if(c[1] == 'x') { + if(Curl_raw_tolower(c[1]) == 'x') { c += 2; /* skip the prefix */ rc = curlx_str_hex(&c, &l, UINT_MAX); + if(rc) + return HOST_NAME; } else rc = curlx_str_octal(&c, &l, UINT_MAX); @@ -520,17 +537,26 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) else rc = curlx_str_number(&c, &l, UINT_MAX); - if(rc) - return HOST_NAME; - - parts[n] = (unsigned int)l; + if(rc) { + if(!n || (rc != STRE_NO_NUM) || *c) + return HOST_NAME; + n--; + } + else + parts[n] = (unsigned int)l; switch(*c) { case '.': - if(n == 3) - return HOST_NAME; - n++; - c++; + if(n == 3) { + if(c[1]) + /* something follows this dot */ + return HOST_NAME; + done = TRUE; + } + else { + n++; + c++; + } break; case '\0': @@ -631,20 +657,24 @@ static CURLUcode parse_authority(struct Curl_URL *u, */ uc = parse_hostname_login(u, auth, authlen, flags, &offset); if(uc) - goto out; + return uc; result = curlx_dyn_addn(host, auth + offset, authlen - offset); if(result) { uc = cc2cu(result); - goto out; + return uc; } uc = parse_port(u, host, has_scheme); if(uc) - goto out; + return uc; if(!curlx_dyn_len(host)) - return CURLUE_NO_HOST; + uc = CURLUE_NO_HOST; + else + uc = urldecode_host(host); + if(uc) + return uc; switch(ipv4_normalize(host)) { case HOST_IPV4: @@ -653,9 +683,7 @@ static CURLUcode parse_authority(struct Curl_URL *u, uc = ipv6_parse(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_NAME: - uc = urldecode_host(host); - if(!uc) - uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); + uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_ERROR: uc = CURLUE_OUT_OF_MEMORY; @@ -665,7 +693,6 @@ static CURLUcode parse_authority(struct Curl_URL *u, break; } -out: return uc; } @@ -855,13 +882,19 @@ UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, path = &url[5]; pathlen = urllen - 5; + /* RFC 8089: file-hier-part = ( "//" auth-path ) / local-path, where + local-path also starts with a "/". So reject anything that does not + start with at least one "/" */ + if(path[0] != '/') + return CURLUE_BAD_FILE_URL; + /* Extra handling URLs with an authority component (i.e. that start with * "file://") * * We allow omitted hostname (e.g. file:/) -- valid according to * RFC 8089, but not the (current) WHAT-WG URL spec. */ - if(path[0] == '/' && path[1] == '/') { + if(path[1] == '/') { /* swallow the two slashes */ const char *ptr = &path[2]; @@ -1006,7 +1039,7 @@ static CURLUcode handle_fragment(CURLU *u, const char *fragment, CURLUcode ures; u->fragment_present = TRUE; if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ + /* skip the leading '#' in the copy but include the null-terminator */ if(flags & CURLU_URLENCODE) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); @@ -1138,8 +1171,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* this pathlen also contains the query and the fragment */ pathlen = urllen - (path - url); if(hostlen) { - ures = parse_authority(u, hostp, hostlen, flags, &host, - u->scheme != NULL); + ures = parse_authority(u, hostp, hostlen, flags, &host, !!u->scheme); if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) ures = guess_scheme(u, &host); } @@ -1212,9 +1244,12 @@ static CURLUcode redirect_url(const char *base, const char *relurl, const char *cutoff = NULL; size_t prelen; CURLUcode uc; + /* this can get here with a NULL u->scheme only if asked to use the default + scheme, so allow fallback to that */ + const char *scheme = u->scheme ? u->scheme : DEFAULT_SCHEME; /* protsep points to the start of the hostname, after [scheme]:// */ - const char *protsep = base + strlen(u->scheme) + 3; + const char *protsep = base + strlen(scheme) + 3; DEBUGASSERT(base && relurl && u); /* all set here */ if(!base) return CURLUE_MALFORMED_INPUT; /* should never happen */ @@ -1417,6 +1452,20 @@ static CURLUcode urlget_format(const CURLU *u, CURLUPart what, return CURLUE_OK; } +static CURLUcode file_url(const CURLU *u, char **part, + const char *fragmentsep, + const char *querysep) +{ + char *url = curl_maprintf("file://%s%s%s%s%s", + u->path, querysep, u->query ? u->query : "", + fragmentsep, u->fragment ? u->fragment : ""); + if(!url) + return CURLUE_OUT_OF_MEMORY; + + *part = url; + return CURLUE_OK; +} + static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) { char *url; @@ -1428,11 +1477,8 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) (u->query_present && flags & CURLU_GET_EMPTY)) ? "?" : ""; char portbuf[7]; - if(u->scheme && curl_strequal("file", u->scheme)) { - url = curl_maprintf("file://%s%s%s%s%s", - u->path, querysep, u->query ? u->query : "", - fragmentsep, u->fragment ? u->fragment : ""); - } + if(curl_strequal("file", u->scheme)) + return file_url(u, part, fragmentsep, querysep); else if(!u->host) return CURLUE_NO_HOST; else { @@ -1449,25 +1495,22 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) return CURLUE_NO_SCHEME; h = Curl_get_scheme(scheme); - if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there is no stored port number, but asked to deliver - a default one for the scheme */ - if(h) { + if(h) { + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there is no stored port number, but asked to deliver a default one + for the scheme */ curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) + else if(port && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + /* there is a stored port number, but asked to inhibit if it matches + the default port for the scheme */ port = NULL; + if(!(h->flags & PROTOPT_URLOPTIONS)) + options = NULL; } - if(h && !(h->flags & PROTOPT_URLOPTIONS)) - options = NULL; - if(u->host[0] == '[') { if(u->zoneid) { /* make it '[ host %25 zoneid ]' */ @@ -1691,8 +1734,11 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, and this is a redirect */ uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); if(!uc) { - /* success, meaning the "" is a fine relative URL, but nothing - changes */ + /* success, meaning the "" is a fine relative URL, and the new URL + inherits scheme/authority/path/query, but not fragment, from the + existing URL (RFC 3986 section 5.2.2) */ + curlx_safefree(u->fragment); + u->fragment_present = FALSE; curlx_free(oldurl); return CURLUE_OK; } @@ -1707,8 +1753,9 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, return parseurl_and_replace(url, u, flags); /* if the old URL is incomplete (we cannot get an absolute URL in - 'oldurl'), replace the existing with the new */ - uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); + 'oldurl'), replace the existing with the new. + Always include "scheme://" to make the URL "complete" */ + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags& ~CURLU_NO_GUESS_SCHEME); if(uc == CURLUE_OUT_OF_MEMORY) return uc; else if(uc) @@ -1794,6 +1841,126 @@ static bool allowed_in_path(unsigned char x) return FALSE; } +static CURLUcode url_encode_part(struct dynbuf *encp, + const char *part, + bool plusencode, + bool pathmode, + bool equalsencode) +{ + const unsigned char *i; + + for(i = (const unsigned char *)part; *i; i++) { + CURLcode result; + if((*i == ' ') && plusencode) + result = curlx_dyn_addn(encp, "+", 1); + else if(ISUNRESERVED(*i) || + (pathmode && allowed_in_path(*i)) || + ((*i == '=') && equalsencode)) { + if((*i == '=') && equalsencode) + /* only skip the first equals sign */ + equalsencode = FALSE; + result = curlx_dyn_addn(encp, i, 1); + } + else { + unsigned char out[3] = { '%' }; + Curl_hexbyte(&out[1], *i); + result = curlx_dyn_addn(encp, out, 3); + } + if(result) + return cc2cu(result); + } + return CURLUE_OK; +} + +static CURLUcode url_uppercasehex_part(struct dynbuf *encp, + const char *part) +{ + char *p; + CURLcode result = curlx_dyn_add(encp, part); + if(result) + return cc2cu(result); + p = curlx_dyn_ptr(encp); + while(*p) { + /* make sure percent encoded are upper case */ + if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && + (ISLOWER(p[1]) || ISLOWER(p[2]))) { + p[1] = Curl_raw_toupper(p[1]); + p[2] = Curl_raw_toupper(p[2]); + p += 3; + } + else + p++; + } + return CURLUE_OK; +} + +static CURLUcode url_append_query(CURLU *u, struct dynbuf *encp) +{ + /* Append the 'encp' string onto the old query. Add a '&' separator if none + is already present at the end of the existing query */ + + size_t querylen = u->query ? strlen(u->query) : 0; + bool addamperand = querylen && (u->query[querylen - 1] != '&'); + if(querylen) { + struct dynbuf qbuf; + CURLcode result; + const char *newp = curlx_dyn_ptr(encp); + curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); + + /* add original query */ + result = curlx_dyn_addn(&qbuf, u->query, querylen); + if(!result && addamperand) + /* add ampersand */ + result = curlx_dyn_addn(&qbuf, "&", 1); + if(!result) + /* add new query part */ + result = curlx_dyn_add(&qbuf, newp); + if(result) + goto nomem; + curlx_dyn_free(encp); + curlx_free(u->query); + u->query = curlx_dyn_ptr(&qbuf); + return CURLUE_OK; +nomem: + curlx_dyn_free(encp); + return cc2cu(result); + } + else { + curlx_free(u->query); + u->query = curlx_dyn_ptr(encp); + } + return CURLUE_OK; +} + +static CURLUcode url_sethost(CURLU *u, struct dynbuf *encp, + bool urlencode, + unsigned int flags) +{ + size_t n = curlx_dyn_len(encp); + bool bad = FALSE; + char *newp = curlx_dyn_ptr(encp); + if(!n) + /* an empty hostname is okay if told so */ + bad = (flags & CURLU_NO_AUTHORITY) ? FALSE : TRUE; + else if(!urlencode) { + /* if the hostname part was not URL encoded here, it was set already URL + encoded so we need to decode it to check */ + size_t dlen; + char *decoded = NULL; + CURLcode result = Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); + if(result || hostname_check(u, decoded, dlen)) + bad = TRUE; + curlx_free(decoded); + } + else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) + bad = TRUE; + if(bad) { + curlx_dyn_free(encp); + return CURLUE_BAD_HOSTNAME; + } + return CURLUE_OK; +} + CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags) { @@ -1867,8 +2034,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } DEBUGASSERT(storep); { - const char *newp; + const char *newp = NULL; struct dynbuf enc; + CURLUcode status; curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); if(leadingslash && (part[0] != '/')) { @@ -1876,112 +2044,20 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(result) return cc2cu(result); } - if(urlencode) { - const unsigned char *i; - - for(i = (const unsigned char *)part; *i; i++) { - CURLcode result; - if((*i == ' ') && plusencode) { - result = curlx_dyn_addn(&enc, "+", 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - else if(ISUNRESERVED(*i) || - (pathmode && allowed_in_path(*i)) || - ((*i == '=') && equalsencode)) { - if((*i == '=') && equalsencode) - /* only skip the first equals sign */ - equalsencode = FALSE; - result = curlx_dyn_addn(&enc, i, 1); - if(result) - return cc2cu(result); - } - else { - unsigned char out[3] = { '%' }; - Curl_hexbyte(&out[1], *i); - result = curlx_dyn_addn(&enc, out, 3); - if(result) - return cc2cu(result); - } - } - } - else { - char *p; - CURLcode result = curlx_dyn_add(&enc, part); - if(result) - return cc2cu(result); - p = curlx_dyn_ptr(&enc); - while(*p) { - /* make sure percent encoded are lower case */ - if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && - (ISUPPER(p[1]) || ISUPPER(p[2]))) { - p[1] = Curl_raw_tolower(p[1]); - p[2] = Curl_raw_tolower(p[2]); - p += 3; - } - else - p++; - } - } - newp = curlx_dyn_ptr(&enc); - - if(appendquery && newp) { - /* Append the 'newp' string onto the old query. Add a '&' separator if - none is present at the end of the existing query already */ - - size_t querylen = u->query ? strlen(u->query) : 0; - bool addamperand = querylen && (u->query[querylen - 1] != '&'); - if(querylen) { - struct dynbuf qbuf; - curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); - - if(curlx_dyn_addn(&qbuf, u->query, querylen)) /* add original query */ - goto nomem; - - if(addamperand) { - if(curlx_dyn_addn(&qbuf, "&", 1)) - goto nomem; - } - if(curlx_dyn_add(&qbuf, newp)) - goto nomem; - curlx_dyn_free(&enc); - curlx_free(*storep); - *storep = curlx_dyn_ptr(&qbuf); - return CURLUE_OK; -nomem: - curlx_dyn_free(&enc); - return CURLUE_OUT_OF_MEMORY; - } - } - - else if(what == CURLUPART_HOST) { - size_t n = curlx_dyn_len(&enc); - if(!n && (flags & CURLU_NO_AUTHORITY)) { - /* Skip hostname check, it is allowed to be empty. */ - } - else { - bool bad = FALSE; - if(!n) - bad = TRUE; /* empty hostname is not okay */ - else if(!urlencode) { - /* if the hostname part was not URL encoded here, it was set ready - URL encoded so we need to decode it to check */ - size_t dlen; - char *decoded = NULL; - CURLcode result = - Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); - if(result || hostname_check(u, decoded, dlen)) - bad = TRUE; - curlx_free(decoded); - } - else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) - bad = TRUE; - if(bad) { - curlx_dyn_free(&enc); - return CURLUE_BAD_HOSTNAME; - } - } + if(urlencode) + status = url_encode_part(&enc, part, plusencode, pathmode, equalsencode); + else + status = url_uppercasehex_part(&enc, part); + if(!status) { + newp = curlx_dyn_ptr(&enc); + + if(appendquery && newp) + return url_append_query(u, &enc); + else if(what == CURLUPART_HOST) + status = url_sethost(u, &enc, urlencode, flags); } + if(status) + return status; curlx_free(*storep); *storep = (char *)CURL_UNCONST(newp); @@ -2001,6 +2077,9 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) if(href->host) { if(!curl_strequal(base->host, href->host)) return FALSE; + if(!curl_strequal(base->zoneid ? base->zoneid : "", + href->zoneid ? href->zoneid : "")) + return FALSE; if(!curl_strequal(base->port, href->port)) { /* This may still match if only one has an explicit port * and it is the default for the scheme. */ diff --git a/deps/curl/lib/urldata.h b/deps/curl/lib/urldata.h index f35bfee053..d4d336d8db 100644 --- a/deps/curl/lib/urldata.h +++ b/deps/curl/lib/urldata.h @@ -55,11 +55,14 @@ #include "asyn.h" #include "cookie.h" +#include "creds.h" #include "psl.h" #include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" +#include "peer.h" +#include "proxy.h" #include "splay.h" #include "curlx/dynbuf.h" #include "bufref.h" @@ -67,6 +70,7 @@ #include "request.h" #include "ratelimit.h" #include "netrc.h" +#include "vtls/vtls_config.h" /* On error return, the value of `pnwritten` has no meaning */ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -138,62 +142,6 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -struct ssl_primary_config { - char *CApath; /* certificate directory (does not work on Windows) */ - char *CAfile; /* certificate to verify peer against */ - char *issuercert; /* optional issuer certificate filename */ - char *clientcert; - char *cipher_list; /* list of ciphers to use */ - char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ - char *signature_algorithms; /* list of signature algorithms to use */ - char *pinned_key; - char *CRLfile; /* CRL to check certificate revocation */ - struct curl_blob *cert_blob; - struct curl_blob *ca_info_blob; - struct curl_blob *issuercert_blob; -#ifdef USE_TLS_SRP - char *username; /* TLS username (for, e.g., SRP) */ - char *password; /* TLS password (for, e.g., SRP) */ -#endif - char *curves; /* list of curves to use */ - uint32_t version_max; /* max supported version the client wants to use */ - uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - uint8_t version; /* what version the client wants to use */ - BIT(verifypeer); /* set TRUE if this is desired */ - BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ - BIT(verifystatus); /* set TRUE if certificate status must be checked */ - BIT(cache_session); /* cache session or not */ -}; - -struct ssl_config_data { - struct ssl_primary_config primary; - long certverifyresult; /* result from the certificate verification */ - curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ - void *fsslctxp; /* parameter for call back */ - char *cert_type; /* format for certificate (default: PEM) */ - char *key; /* private key filename */ - struct curl_blob *key_blob; - char *key_type; /* format for private key (default: PEM) */ - char *key_passwd; /* plain text private key password */ - BIT(certinfo); /* gather lots of certificate info */ - BIT(earlydata); /* use TLS 1.3 early data */ - BIT(enable_beast); /* allow this flaw for interoperability's sake */ - BIT(no_revoke); /* disable SSL certificate revocation checks */ - BIT(no_partialchain); /* do not accept partial certificate chains */ - BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation - list errors */ - BIT(native_ca_store); /* use the native CA store of operating system */ - BIT(auto_client_cert); /* automatically locate and use a client - certificate for authentication (Schannel) */ - BIT(custom_cafile); /* application has set custom CA file */ - BIT(custom_capath); /* application has set custom CA path */ - BIT(custom_cablob); /* application has set custom CA blob */ -}; - -struct ssl_general_config { - int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ -}; - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif @@ -201,14 +149,12 @@ struct ssl_general_config { #ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { + struct Curl_creds *creds; + struct Curl_peer *origin; #ifdef USE_WINDOWS_SSPI BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* copy of user/passwd used to make the identity for http_context. - either may be NULL. */ - char *user; - char *passwd; #else char *nonce; char *cnonce; @@ -246,23 +192,12 @@ typedef enum { struct ConnectBits { BIT(connect_only); #ifndef CURL_DISABLE_PROXY - BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ - BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ - BIT(proxy_user_passwd); /* user+password for the proxy? */ - BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. - This is implicit when SSL-protocols are used through - proxies, but can also be enabled explicitly by - apps */ - BIT(proxy); /* if set, this transfer is done through a proxy - any type */ + BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */ #endif /* always modify bits.close with the connclose() and connkeep() macros! */ BIT(close); /* if set, we close the connection after this request */ BIT(reuse); /* if set, this is a reused connection */ BIT(altused); /* this is an alt-svc "redirect" */ - BIT(conn_to_host); /* if set, this connection has a "connect to host" - that overrides the host in the URL */ - BIT(conn_to_port); /* if set, this connection has a "connect to port" - that overrides the port in the URL (remote port) */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -278,10 +213,6 @@ struct ConnectBits { EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ - BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ -#endif -#ifndef CURL_DISABLE_NETRC - BIT(netrc); /* name+password provided by netrc */ #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ @@ -289,9 +220,6 @@ struct ConnectBits { BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifdef USE_UNIX_SOCKETS - BIT(abstract_unix_socket); -#endif BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with accept() */ BIT(parallel_connect); /* set TRUE when a parallel connect attempt has @@ -320,6 +248,8 @@ struct hostname { #define TRNSPRT_QUIC 5 #define TRNSPRT_UNIX 6 +#define TRNSPRT_IS_DGRAM(x) (((x) == TRNSPRT_UDP) || ((x) == TRNSPRT_QUIC)) + struct ip_quadruple { char remote_ip[MAX_IPADR_LEN]; char local_ip[MAX_IPADR_LEN]; @@ -333,14 +263,6 @@ struct ip_quadruple { ((x)->transport == TRNSPRT_UDP) || \ ((x)->transport == TRNSPRT_QUIC)) -struct proxy_info { - struct hostname host; - uint16_t port; - uint8_t proxytype; /* what kind of proxy that is in use */ - char *user; /* proxy username string, allocated */ - char *passwd; /* proxy password string, allocated */ -}; - /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. @@ -369,19 +291,18 @@ struct connectdata { * the connection is cleaned up (see Curl_hash_add2()).*/ struct Curl_hash meta_hash; - struct hostname host; - char *secondaryhostname; /* secondary socket hostname (ftp) */ - struct hostname conn_to_host; /* the host to connect to. valid only if - bits.conn_to_host is set */ + /* Who the connection is talking to, ultimately */ + struct Curl_peer *origin; /* connection ultimately talks to this */ + struct Curl_peer *via_peer; /* if set, connection really talks to this */ + struct Curl_peer *origin2; /* origin of SECONDARYSOCKET */ + struct Curl_peer *via_peer2; /* peer of SECONDARYSOCKET */ #ifndef CURL_DISABLE_PROXY struct proxy_info socks_proxy; struct proxy_info http_proxy; #endif - char *user; /* username string, allocated */ - char *passwd; /* password string, allocated */ + struct Curl_creds *creds; /* When connection itself is tied to credentials */ + struct Curl_peer *creds_origin; /* origin tied credentials are for */ char *options; /* options string, allocated */ - char *sasl_authzid; /* authorization identity string, allocated */ - char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ @@ -425,7 +346,7 @@ struct connectdata { /*************** Request - specific items ************/ #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - CtxtHandle *sslContext; /* mingw-w64 v9+. MS SDK 7.0A+. */ + CtxtHandle *sslContext; /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #endif #ifdef USE_NTLM @@ -438,10 +359,6 @@ struct connectdata { curlnegotiate proxy_negotiate_state; #endif -#ifdef USE_UNIX_SOCKETS - char *unix_domain_socket; -#endif - /* When this connection is created, store the conditions for the local end bind. This is stored before the actual bind and before any connection is made and will serve the purpose of being used for comparison reasons so @@ -456,14 +373,8 @@ struct connectdata { #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif - /* The field below gets set in connect.c:connecthost() */ - uint16_t remote_port; /* the remote port, not the proxy port! */ - uint16_t conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ uint16_t localportrange; uint16_t localport; - uint16_t secondary_port; /* secondary socket remote port to connect to - (ftp) */ uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily the transport the connection ends using due to Alt-Svc and happy eyeballing. Use Curl_conn_get_transport() for actual value once the @@ -477,14 +388,13 @@ struct connectdata { #ifndef CURL_DISABLE_PROXY #define CURL_CONN_HOST_DISPNAME(c) \ - ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ - (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname) + ((c)->socks_proxy.peer ? (c)->socks_proxy.peer->user_hostname : \ + (c)->http_proxy.peer ? (c)->http_proxy.peer->user_hostname : \ + (c)->via_peer ? (c)->via_peer->user_hostname : \ + (c)->origin->user_hostname) #else #define CURL_CONN_HOST_DISPNAME(c) \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname + ((c)->via_peer ? (c)->via_peer->user_hostname : (c)->origin->user_hostname) #endif /* The end of connectdata. */ @@ -517,9 +427,6 @@ struct PureInfo { session handle without disturbing information which is still alive, and that might be reused, in the connection pool. */ struct ip_quadruple primary; - int conn_remote_port; /* this is the "remote port", which is the port - number of the used URL, independent of proxy or - not */ const char *conn_scheme; uint32_t conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with @@ -604,8 +511,6 @@ struct auth { actual request */ BIT(multipass); /* TRUE if this is not yet authenticated but within the auth multipass negotiation */ - BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it - should be RFC compliant */ }; #ifdef USE_NGHTTP2 @@ -620,15 +525,7 @@ struct Curl_data_prio_node { * on the same connection. */ struct Curl_data_priority { -#ifdef USE_NGHTTP2 - /* tree like dependencies only implemented in nghttp2 */ - struct Curl_easy *parent; - struct Curl_data_prio_node *children; -#endif int weight; -#ifdef USE_NGHTTP2 - BIT(exclusive); -#endif }; /* Timers */ @@ -672,11 +569,6 @@ struct urlpieces { char *query; }; -#define CREDS_NONE 0 -#define CREDS_URL 1 /* from URL */ -#define CREDS_OPTION 2 /* set with a CURLOPT_ */ -#define CREDS_NETRC 3 /* found in netrc */ - struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ @@ -692,13 +584,14 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* hostname, port number and protocol of the first (not followed) request. - if set, this should be the hostname that we will sent authorization to, - no else. Used to make Location: following not keep sending user+password. - This is strdup()ed data. */ - char *first_host; - int first_remote_port; - curl_prot_t first_remote_protocol; + /* Origin of the initial (e.g. not followed) request of a transfer. + Credentials from CURLOPT_* are only valid for this origin. + Always set once a transfer starts searching for connections. */ + struct Curl_peer *initial_origin; + /* Current origin of the transfer, changes to origin of follow + * requests. */ + struct Curl_peer *origin; + int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL @@ -706,6 +599,7 @@ struct UrlState { void (*prev_signal)(int sig); #endif #ifndef CURL_DISABLE_DIGEST_AUTH + char *envproxy; /* last proxy string used for proxy-related state */ struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif @@ -787,6 +681,8 @@ struct UrlState { struct store_netrc netrc; #endif + struct Curl_creds *creds; /* Credentials for the origin only */ + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { @@ -798,14 +694,6 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif - - /* transfer credentials */ - char *user; - char *passwd; -#ifndef CURL_DISABLE_PROXY - char *proxyuser; - char *proxypasswd; -#endif } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; @@ -815,14 +703,8 @@ struct UrlState { CONN_MAX_RETRIES */ uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - unsigned int creds_from:2; /* where is the server credentials originating - from, see the CREDS_* defines above */ - - /* when curl_easy_perform() is called, the multi handle is "owned" by - the easy handle so curl_easy_cleanup() on such an easy handle will - also close the multi handle! */ - BIT(multi_owned_by_easy); + BIT(really_alive); /* transfer is really alive in multi, passed INIT */ BIT(this_is_a_follow); /* this is a followed Location: request */ BIT(refused_stream); /* this was refused, try again */ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. @@ -929,7 +811,7 @@ enum dupstring { STRING_SET_REFERER, /* custom string for the HTTP referer field */ STRING_SET_URL, /* what original URL to work on */ STRING_USERAGENT, /* User-Agent string */ - STRING_SSL_ENGINE, /* name of ssl engine */ + STRING_SSL_ENGINE, /* name of SSL engine */ STRING_USERNAME, /* , if used */ STRING_PASSWORD, /* , if used */ STRING_OPTIONS, /* , if used */ @@ -1177,9 +1059,6 @@ struct UserDefined { struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif uint32_t maxconnects; /* Max idle connections in the connection cache */ -#ifdef USE_ECH - int tls_ech; /* TLS ECH configuration */ -#endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ uint16_t expect_100_timeout; /* in milliseconds */ @@ -1194,6 +1073,9 @@ struct UserDefined { #ifndef CURL_DISABLE_TFTP uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif +#ifdef USE_ECH + uint8_t tls_ech; /* TLS ECH configuration */ +#endif #ifndef CURL_DISABLE_NETRC uint8_t use_netrc; /* enum CURL_NETRC_OPTION values */ #endif @@ -1213,9 +1095,8 @@ struct UserDefined { uint8_t ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ uint8_t upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ -#ifdef HAVE_GSSAPI - /* GSS-API credential delegation, see the documentation of - CURLOPT_GSSAPI_DELEGATION */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS-API/SSPI credential delegation, see CURLOPT_GSSAPI_DELEGATION */ uint8_t gssapi_delegation; #endif uint8_t http_follow_mode; /* follow HTTP redirects */ diff --git a/deps/curl/lib/vauth/cleartext.c b/deps/curl/lib/vauth/cleartext.c index 7976adec9c..ca0c9967ff 100644 --- a/deps/curl/lib/vauth/cleartext.c +++ b/deps/curl/lib/vauth/cleartext.c @@ -40,24 +40,21 @@ * * Parameters: * - * authzid [in] - The authorization identity. - * authcid [in] - The authentication identity. + * creds [in] - The credentials. * passwd [in] - The password. * out [out] - The result storage. * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out) { size_t len; char *auth; - size_t zlen = (authzid == NULL ? 0 : strlen(authzid)); - size_t clen = strlen(authcid); - size_t plen = strlen(passwd); + size_t zlen = strlen(Curl_creds_sasl_authzid(creds)); + size_t clen = strlen(Curl_creds_user(creds)); + size_t plen = strlen(Curl_creds_passwd(creds)); if((zlen > CURL_MAX_INPUT_LENGTH) || (clen > CURL_MAX_INPUT_LENGTH) || (plen > CURL_MAX_INPUT_LENGTH)) @@ -65,8 +62,10 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, len = zlen + clen + plen + 2; - auth = curl_maprintf("%s%c%s%c%s", authzid ? authzid : "", '\0', - authcid, '\0', passwd); + auth = curl_maprintf("%s%c%s%c%s", + Curl_creds_sasl_authzid(creds), '\0', + Curl_creds_user(creds), '\0', + Curl_creds_passwd(creds)); if(!auth) return CURLE_OUT_OF_MEMORY; Curl_bufref_set(out, auth, len, curl_free); diff --git a/deps/curl/lib/vauth/cram.c b/deps/curl/lib/vauth/cram.c index d3f2d137b7..023c31b58b 100644 --- a/deps/curl/lib/vauth/cram.c +++ b/deps/curl/lib/vauth/cram.c @@ -47,18 +47,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out) { struct HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char *response; + const char *user = Curl_creds_user(creds); + const char *passwd = Curl_creds_passwd(creds); /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, - (const unsigned char *)passwdp, - curlx_uztoui(strlen(passwdp))); + (const unsigned char *)passwd, + curlx_uztoui(strlen(passwd))); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -73,7 +74,7 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, /* Generate the response */ response = curl_maprintf( "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - userp, digest[0], digest[1], digest[2], digest[3], digest[4], + user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); if(!response) diff --git a/deps/curl/lib/vauth/digest.c b/deps/curl/lib/vauth/digest.c index 5ecfd60aad..1818fc96c7 100644 --- a/deps/curl/lib/vauth/digest.c +++ b/deps/curl/lib/vauth/digest.c @@ -36,6 +36,7 @@ #include "curl_sha512_256.h" #include "curlx/strparse.h" #include "rand.h" +#include "escape.h" #ifndef USE_WINDOWS_SSPI #define SESSION_ALGO 1 /* for algos with this bit set */ @@ -163,6 +164,11 @@ static char *auth_digest_string_quoted(const char *s) if(!result) result = curlx_dyn_addn(&out, s, 1); } + else if((*s < ' ') || (*s > 0x7e)) { + unsigned char buf[3] = { '%' }; + Curl_hexbyte(&buf[1], (unsigned char)*s); + result = curlx_dyn_addn(&out, buf, 3); + } else result = curlx_dyn_addn(&out, s, 1); if(result) @@ -231,7 +237,7 @@ static bool auth_digest_get_key_value(const char *chlg, const char *key, static void auth_digest_get_qop_values(const char *options, int *value) { struct Curl_str out; - /* Initialise the output */ + /* Initialize the output */ *value = 0; while(!curlx_str_until(&options, &out, 32, ',')) { @@ -332,13 +338,16 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out) { + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t i; struct MD5_context *ctxt; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; @@ -370,7 +379,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return result; /* We only support md5 sessions */ - if(strcmp(algorithm, "md5-sess") != 0) + if(strcmp(algorithm, "md5-sess")) return CURLE_BAD_CONTENT_ENCODING; /* Get the qop-values from the qop-options */ @@ -418,7 +427,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); if(!spn) return CURLE_OUT_OF_MEMORY; @@ -517,7 +526,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(digest->nonce) before = TRUE; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_digest_cleanup(digest); for(;;) { @@ -666,8 +675,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The username. - * passwdp [in] - The user's password. + * creds [in] - The credentials * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. @@ -679,8 +687,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, */ static CURLcode auth_create_digest_http_message( struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -689,6 +696,8 @@ static CURLcode auth_create_digest_http_message( CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); unsigned char hashbuf[32]; /* 32 bytes/256 bits */ unsigned char request_digest[65]; unsigned char ha1[65]; /* 64 digits and 1 zero byte */ @@ -835,7 +844,8 @@ static CURLcode auth_create_digest_http_message( if(digest->qop) hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, - digest->nc, digest->cnonce, digest->qop, ha2); + (unsigned int)digest->nc, digest->cnonce, + digest->qop, ha2); else hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); @@ -900,7 +910,7 @@ static CURLcode auth_create_digest_http_message( nonce_quoted, uri_quoted, digest->cnonce, - digest->nc, + (unsigned int)digest->nc, digest->qop, request_digest); @@ -986,29 +996,28 @@ static CURLcode auth_create_digest_http_message( * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { if(digest->algo <= ALGO_MD5SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_md5_to_ascii, Curl_md5it); if(digest->algo <= ALGO_SHA256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, Curl_sha256it); #ifdef CURL_HAVE_SHA512_256 if(digest->algo <= ALGO_SHA512_256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, @@ -1031,6 +1040,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, */ void Curl_auth_digest_cleanup(struct digestdata *digest) { + Curl_peer_unlink(&digest->origin); + Curl_creds_unlink(&digest->creds); curlx_safefree(digest->nonce); curlx_safefree(digest->cnonce); curlx_safefree(digest->realm); diff --git a/deps/curl/lib/vauth/digest_sspi.c b/deps/curl/lib/vauth/digest_sspi.c index f0b6780fca..305e367e17 100644 --- a/deps/curl/lib/vauth/digest_sspi.c +++ b/deps/curl/lib/vauth/digest_sspi.c @@ -28,6 +28,7 @@ #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) +#include "creds.h" #include "vauth/vauth.h" #include "vauth/digest.h" #include "curlx/multibyte.h" @@ -83,9 +84,8 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out) { CURLcode result = CURLE_OK; @@ -103,6 +103,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; /* Ensure we have a valid challenge message */ if(!Curl_bufref_len(chlg)) { @@ -131,15 +133,16 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); if(!spn) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &identity); if(result) { curlx_free(spn); curlx_free(output_token); @@ -381,8 +384,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -421,16 +423,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* If the user/passwd that was used to make the identity for http_context has changed then delete that context. */ - if((userp && !digest->user) || (!userp && digest->user) || - (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || - (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || - (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { + if(!Curl_creds_same(creds, digest->creds)) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } if(digest->http_context) { @@ -458,7 +456,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(status == SEC_E_OK) output_token_len = chlg_buf[4].cbBuffer; else { /* delete the context so a new one can be made */ - infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status); + infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", + (unsigned long)status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } @@ -473,13 +472,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, unsigned long attrs; TCHAR *spn; - /* free the copy of user/passwd used to make the previous identity */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + /* free the credentials used to make the previous identity */ + Curl_creds_unlink(&digest->creds); - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - if(Curl_create_sspi_identity(userp, passwdp, &identity)) { + if(Curl_create_sspi_identity(creds->user, creds->passwd, &identity)) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -499,26 +497,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Use the current Windows user */ p_identity = NULL; - if(userp) { - digest->user = curlx_strdup(userp); - - if(!digest->user) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - return CURLE_OUT_OF_MEMORY; - } - } - - if(passwdp) { - digest->passwd = curlx_strdup(passwdp); - - if(!digest->passwd) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - curlx_safefree(digest->user); - return CURLE_OUT_OF_MEMORY; - } - } + if(creds) + Curl_creds_link(&digest->creds, creds); /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, @@ -577,11 +557,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Generate our response message */ status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, - spn, - ISC_REQ_USE_HTTP_STYLE, 0, 0, - &chlg_desc, 0, - digest->http_context, - &resp_desc, &attrs, NULL); + spn, + ISC_REQ_USE_HTTP_STYLE, + 0, 0, + &chlg_desc, 0, + digest->http_context, + &resp_desc, &attrs, NULL); curlx_free(spn); if(status == SEC_I_COMPLETE_NEEDED || @@ -649,8 +630,8 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } /* Free the copy of user/passwd used to make the identity for http_context */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); + Curl_peer_unlink(&digest->origin); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/deps/curl/lib/vauth/gsasl.c b/deps/curl/lib/vauth/gsasl.c index 958f4ffab7..10a83fdb09 100644 --- a/deps/curl/lib/vauth/gsasl.c +++ b/deps/curl/lib/vauth/gsasl.c @@ -32,6 +32,10 @@ #include +#if GSASL_VERSION_NUMBER < 0x010600 +#error "requires libgsasl 1.6.0+" +#endif + bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, const char *mech, struct gsasldata *gsasl) @@ -47,6 +51,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client); if(res != GSASL_OK) { gsasl_done(gsasl->ctx); + gsasl->ctx = NULL; return FALSE; } @@ -54,15 +59,14 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, } CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl) { #if GSASL_VERSION_NUMBER >= 0x010b00 int res; res = #endif - gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); + gsasl_property_set(gsasl->client, GSASL_AUTHID, creds->user); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting AUTHID failed: %s", gsasl_strerror(res)); @@ -73,7 +77,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, #if GSASL_VERSION_NUMBER >= 0x010b00 res = #endif - gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); + gsasl_property_set(gsasl->client, GSASL_PASSWORD, creds->passwd); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting PASSWORD failed: %s", gsasl_strerror(res)); diff --git a/deps/curl/lib/vauth/krb5_gssapi.c b/deps/curl/lib/vauth/krb5_gssapi.c index 64c735be58..738ce9a744 100644 --- a/deps/curl/lib/vauth/krb5_gssapi.c +++ b/deps/curl/lib/vauth/krb5_gssapi.c @@ -74,9 +74,8 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -89,9 +88,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, OM_uint32 unused_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - - (void)userp; - (void)passwdp; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; diff --git a/deps/curl/lib/vauth/krb5_sspi.c b/deps/curl/lib/vauth/krb5_sspi.c index e7491be022..543a6fe284 100644 --- a/deps/curl/lib/vauth/krb5_sspi.c +++ b/deps/curl/lib/vauth/krb5_sspi.c @@ -79,9 +79,8 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -97,6 +96,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { /* Generate our SPN */ @@ -128,9 +129,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &krb5->identity); if(result) return result; @@ -152,8 +154,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, krb5->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_safefree(krb5->credentials); return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ krb5->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -287,10 +291,13 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, input_buf[1].pvBuffer = NULL; input_buf[1].cbBuffer = 0; - /* Decrypt the inbound challenge and obtain the qop */ + /* Decrypt the inbound challenge and obtain the qop. The encrypted message + is decrypted in place, overwriting the original contents of its buffer. + The SECBUFFER_DATA receives a pointer to the message in + SECBUFFER_STREAM. */ status = Curl_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); if(status != SEC_E_OK) { - infof(data, "GSSAPI handshake failure (empty security message)"); + infof(data, "GSSAPI handshake failure (decryption failed)"); return CURLE_BAD_CONTENT_ENCODING; } @@ -306,9 +313,6 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, max_size = ((unsigned long)indata[1] << 16) | ((unsigned long)indata[2] << 8) | indata[3]; - /* Free the challenge as it is not required anymore */ - Curl_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); - /* Process the security layer */ if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { infof(data, "GSSAPI handshake failure (invalid security layer)"); @@ -428,15 +432,13 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) /* Free our security context */ if(krb5->context) { Curl_pSecFn->DeleteSecurityContext(krb5->context); - curlx_free(krb5->context); - krb5->context = NULL; + curlx_safefree(krb5->context); } /* Free our credentials handle */ if(krb5->credentials) { Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); - curlx_free(krb5->credentials); - krb5->credentials = NULL; + curlx_safefree(krb5->credentials); } /* Free our identity */ diff --git a/deps/curl/lib/vauth/ntlm.c b/deps/curl/lib/vauth/ntlm.c index 1e485ed34d..2803c05d9f 100644 --- a/deps/curl/lib/vauth/ntlm.c +++ b/deps/curl/lib/vauth/ntlm.c @@ -361,8 +361,8 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, ntlm->flags = 0; if((type2len < 32) || - (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || - (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { + memcmp(type2, NTLMSSP_SIGNATURE, 8) || + memcmp(type2 + 8, type2_marker, sizeof(type2_marker))) { /* This was not a good enough type-2 message */ infof(data, "NTLM handshake failure (bad type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; @@ -421,9 +421,8 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -442,6 +441,8 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (*) -> Optional */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t size; char *ntlmbuf; @@ -453,12 +454,11 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size_t domoff = hostoff + hostlen; /* This is 0: remember that host and domain are empty */ (void)data; - (void)userp; - (void)passwdp; + (void)creds; (void)service; (void)host; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); ntlmbuf = curl_maprintf(NTLMSSP_SIGNATURE "%c" @@ -542,8 +542,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -579,6 +578,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* The fixed hostname we provide, in order to not leak our real local host name. Copy the name used by Firefox. */ static const char host[] = "WORKSTATION"; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); const char *user; const char *domain = ""; size_t hostoff = 0; diff --git a/deps/curl/lib/vauth/ntlm_sspi.c b/deps/curl/lib/vauth/ntlm_sspi.c index 4c41eb21f4..2e98e86e5f 100644 --- a/deps/curl/lib/vauth/ntlm_sspi.c +++ b/deps/curl/lib/vauth/ntlm_sspi.c @@ -76,9 +76,8 @@ bool Curl_auth_is_ntlm_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -88,8 +87,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SecBufferDesc type_1_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); /* Query the security package for NTLM */ @@ -111,11 +112,12 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, if(!ntlm->output_token) return CURLE_OUT_OF_MEMORY; - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { CURLcode result; /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &ntlm->identity); if(result) return result; @@ -137,8 +139,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, ntlm->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_safefree(ntlm->credentials); return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ ntlm->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -227,8 +231,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -240,8 +243,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SECURITY_STATUS status; unsigned long attrs; - (void)passwdp; - (void)userp; + (void)creds; /* Setup the type-2 "input" security buffer */ type_2_desc.ulVersion = SECBUFFER_VERSION; @@ -252,7 +254,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from schannel. + /* SSL context comes from schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc * otherwise IIS does not pass the authentication (401 response). @@ -296,7 +298,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, &attrs, NULL); if(status != SEC_E_OK) { infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", - status); + (unsigned long)status); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -325,15 +327,13 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) /* Free our security context */ if(ntlm->context) { Curl_pSecFn->DeleteSecurityContext(ntlm->context); - curlx_free(ntlm->context); - ntlm->context = NULL; + curlx_safefree(ntlm->context); } /* Free our credentials handle */ if(ntlm->credentials) { Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); - curlx_free(ntlm->credentials); - ntlm->credentials = NULL; + curlx_safefree(ntlm->credentials); } /* Free our identity */ diff --git a/deps/curl/lib/vauth/oauth2.c b/deps/curl/lib/vauth/oauth2.c index 4541d1d551..f597114638 100644 --- a/deps/curl/lib/vauth/oauth2.c +++ b/deps/curl/lib/vauth/oauth2.c @@ -47,21 +47,22 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out) { char *oauth; /* Generate the message */ if(port == 0 || port == 80) - oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, - bearer); + oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), host, + Curl_creds_oauth_bearer(creds)); else oauth = curl_maprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", - user, host, port, bearer); + Curl_creds_user(creds), host, port, + Curl_creds_oauth_bearer(creds)); if(!oauth) return CURLE_OUT_OF_MEMORY; @@ -83,12 +84,13 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out) { /* Generate the message */ - char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), + Curl_creds_oauth_bearer(creds)); if(!xoauth) return CURLE_OUT_OF_MEMORY; diff --git a/deps/curl/lib/vauth/spnego_gssapi.c b/deps/curl/lib/vauth/spnego_gssapi.c index 38bb4c1422..869a27fdc9 100644 --- a/deps/curl/lib/vauth/spnego_gssapi.c +++ b/deps/curl/lib/vauth/spnego_gssapi.c @@ -70,9 +70,8 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -90,8 +89,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct gss_channel_bindings_struct chan; #endif - (void)user; - (void)password; + (void)creds; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server @@ -105,6 +103,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) return CURLE_OUT_OF_MEMORY; diff --git a/deps/curl/lib/vauth/spnego_sspi.c b/deps/curl/lib/vauth/spnego_sspi.c index 1baf59320a..b7d82c04dd 100644 --- a/deps/curl/lib/vauth/spnego_sspi.c +++ b/deps/curl/lib/vauth/spnego_sspi.c @@ -78,9 +78,8 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -105,6 +104,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->spn) { /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; nego->spn = Curl_auth_build_spn(service, host, NULL); if(!nego->spn) return CURLE_OUT_OF_MEMORY; @@ -133,9 +134,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(user, password, &nego->identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &nego->identity); if(result) return result; @@ -157,8 +159,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, nego->credentials, NULL); - if(nego->status != SEC_E_OK) + if(nego->status != SEC_E_OK) { + curlx_safefree(nego->credentials); return CURLE_AUTH_ERROR; + } /* Allocate our new context handle */ nego->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -189,7 +193,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from Schannel. + /* SSL context comes from Schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc * otherwise IIS does not pass the authentication (401 response). @@ -223,15 +227,20 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(nego->token_max); /* Generate our challenge-response message */ - nego->status = - Curl_pSecFn->InitializeSecurityContext(nego->credentials, - chlg ? nego->context : NULL, - nego->spn, - ISC_REQ_CONFIDENTIALITY, - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, - 0, nego->context, - &resp_desc, &attrs, NULL); + { + DWORD sspi_flags = ISC_REQ_CONFIDENTIALITY; + if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) + sspi_flags |= ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH; + nego->status = + Curl_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : NULL, + nego->spn, + sspi_flags, + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, + 0, nego->context, + &resp_desc, &attrs, NULL); + } /* Free the decoded challenge as it is not required anymore */ curlx_free(chlg); @@ -313,15 +322,13 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) /* Free our security context */ if(nego->context) { Curl_pSecFn->DeleteSecurityContext(nego->context); - curlx_free(nego->context); - nego->context = NULL; + curlx_safefree(nego->context); } /* Free our credentials handle */ if(nego->credentials) { Curl_pSecFn->FreeCredentialsHandle(nego->credentials); - curlx_free(nego->credentials); - nego->credentials = NULL; + curlx_safefree(nego->credentials); } /* Free our identity */ diff --git a/deps/curl/lib/vauth/vauth.c b/deps/curl/lib/vauth/vauth.c index a00078fce1..3259556e5f 100644 --- a/deps/curl/lib/vauth/vauth.c +++ b/deps/curl/lib/vauth/vauth.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "vauth/vauth.h" +#include "creds.h" #include "curlx/multibyte.h" #include "url.h" @@ -111,15 +112,16 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, * * Returns TRUE on success; otherwise FALSE. */ -bool Curl_auth_user_contains_domain(const char *user) +bool Curl_auth_user_contains_domain(struct Curl_creds *creds) { bool valid = FALSE; - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Check we have a domain name or UPN present */ - const char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(creds->user, "\\/@"); - valid = (p != NULL && p > user && p < user + strlen(user) - 1); + valid = p && (p > creds->user) && + (p < (creds->user + strlen(creds->user) - 1)); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else @@ -133,17 +135,18 @@ bool Curl_auth_user_contains_domain(const char *user) /* * Curl_auth_allowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. + * "sensitive data" can be sent to the connection's origin. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) { - struct connectdata *conn = data->conn; - return !data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_host && - curl_strequal(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->scheme->protocol)); + return Curl_auth_allowed_to_origin(data, data->state.origin); +} + +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin) +{ + return data->set.allow_auth_to_other_hosts || + Curl_peer_equal(data->state.initial_origin, origin); } #ifdef USE_NTLM diff --git a/deps/curl/lib/vauth/vauth.h b/deps/curl/lib/vauth/vauth.h index 279da60be2..0f82f92945 100644 --- a/deps/curl/lib/vauth/vauth.h +++ b/deps/curl/lib/vauth/vauth.h @@ -30,7 +30,9 @@ #include "urldata.h" struct Curl_easy; +struct Curl_creds; struct connectdata; +struct Curl_peer; #ifndef CURL_DISABLE_DIGEST_AUTH struct digestdata; @@ -58,6 +60,8 @@ struct gsasldata; * "sensitive data" can (still) be sent to this host. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data); +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin); /* This is used to build an SPN string */ #ifndef USE_WINDOWS_SSPI @@ -69,12 +73,10 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, #endif /* This is used to test if the user contains a Windows domain name */ -bool Curl_auth_user_contains_domain(const char *user); +bool Curl_auth_user_contains_domain(struct Curl_creds *creds); /* This is used to generate a PLAIN cleartext message */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out); /* This is used to generate a LOGIN cleartext message */ @@ -86,8 +88,7 @@ void Curl_auth_create_external_message(const char *user, struct bufref *out); #ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out); /* This is used to evaluate if DIGEST is supported */ @@ -96,9 +97,8 @@ bool Curl_auth_is_digest_supported(void); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out); /* This is used to decode an HTTP DIGEST challenge message */ @@ -107,8 +107,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* This is used to generate an HTTP DIGEST response message */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -140,8 +139,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, struct gsasldata *gsasl); /* This is used to start a gsasl method */ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl); /* This is used to process and generate a new SASL token */ @@ -197,9 +195,8 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out); @@ -211,8 +208,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, /* This is used to generate a base64 encoded NTLM type-3 message */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out); @@ -221,15 +217,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #endif /* USE_NTLM */ /* This is used to generate a base64 encoded OAuth 2.0 message */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out); /* This is used to generate a base64 encoded XOAuth 2.0 message */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out); #ifdef USE_KERBEROS5 @@ -260,9 +254,8 @@ bool Curl_auth_is_gssapi_supported(void); /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token message */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -324,15 +317,13 @@ struct negotiatedata { BIT(havemultiplerequests); }; -struct negotiatedata * -Curl_auth_nego_get(struct connectdata *conn, bool proxy); +struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy); /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego); diff --git a/deps/curl/lib/version.c b/deps/curl/lib/version.c index b3b0a46abb..299caee9fb 100644 --- a/deps/curl/lib/version.c +++ b/deps/curl/lib/version.c @@ -388,7 +388,7 @@ static int idn_present(curl_version_info_data *info) (void)info; return TRUE; #else - return info->libidn != NULL; + return !!info->libidn; #endif } #endif @@ -491,6 +491,9 @@ static const struct feat features_table[] = { #ifdef USE_NTLM FEATURE("NTLM", NULL, CURL_VERSION_NTLM), #endif +#ifdef USE_PROXY_HTTP3 + FEATURE("proxy-HTTP3", NULL, 0), +#endif #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif diff --git a/deps/curl/lib/vquic/capsule.c b/deps/curl/lib/vquic/capsule.c new file mode 100644 index 0000000000..4bbdae3ace --- /dev/null +++ b/deps/curl/lib/vquic/capsule.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#ifdef HAVE_ARPA_INET_H +#include /* for htons() */ +#endif + +#include +#include "urldata.h" +#include "curlx/dynbuf.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "bufq.h" +#include "vquic/capsule.h" + + +/** + * Convert 64-bit value from network byte order to host byte order + */ +static uint64_t capsule_ntohll(uint64_t value) +{ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + return value; +#elif (defined(__GNUC__) || defined(__clang__)) && \ + defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return __builtin_bswap64(value); +#else + union { + uint64_t u64; + uint32_t u32[2]; + } src, dst; + + src.u64 = value; + dst.u32[0] = ntohl(src.u32[1]); + dst.u32[1] = ntohl(src.u32[0]); + return dst.u64; +#endif +} + +/** + * Encode a variable-length integer into a plain buffer. + * @param buf Output buffer (must have at least 8 bytes) + * @param value Value to encode (must be <= 0x3FFFFFFFFFFFFFFF) + * @return Number of bytes written + */ +static size_t capsule_encode_varint_buf(uint8_t *buf, uint64_t value) +{ + DEBUGASSERT(value <= 0x3FFFFFFFFFFFFFFF); + + if(value <= 0x3F) { + buf[0] = (uint8_t)value; + return 1; + } + else if(value <= 0x3FFF) { + uint16_t encoded = (uint16_t)value & 0x3FFF; + encoded = ntohs(encoded | 0x4000); + memcpy(buf, &encoded, 2); + return 2; + } + else if(value <= 0x3FFFFFFF) { + uint32_t encoded = (uint32_t)value & 0x3FFFFFFF; + encoded = ntohl(encoded | 0x80000000); + memcpy(buf, &encoded, 4); + return 4; + } + else { + uint64_t encoded = (uint64_t)value & 0x3FFFFFFFFFFFFFFF; + encoded = capsule_ntohll(encoded | 0xC000000000000000); + memcpy(buf, &encoded, 8); + return 8; + } +} + +static CURLcode capsule_peek_u8(struct bufq *recvbufq, + size_t offset, + uint8_t *pbyte) +{ + const unsigned char *peek = NULL; + size_t peeklen = 0; + + if(!Curl_bufq_peek_at(recvbufq, offset, &peek, &peeklen) || !peeklen) + return CURLE_AGAIN; + *pbyte = peek[0]; + return CURLE_OK; +} + +static CURLcode capsule_decode_varint_at(struct bufq *recvbufq, + size_t offset, + uint64_t *pvalue, + size_t *pconsumed) +{ + uint8_t first_byte, byte; + uint64_t value; + size_t nbytes; + size_t i; + CURLcode result; + + result = capsule_peek_u8(recvbufq, offset, &first_byte); + if(result) + return result; + + nbytes = (size_t)1 << (first_byte >> 6); /* 1, 2, 4 or 8 bytes */ + value = first_byte & 0x3F; + + for(i = 1; i < nbytes; ++i) { + result = capsule_peek_u8(recvbufq, offset + i, &byte); + if(result) + return result; + value = (value << 8) | byte; + } + + *pvalue = value; + *pconsumed = nbytes; + return CURLE_OK; +} + +/** + * Write the capsule header (type + varint length + context ID) into `hdr`. + * @param hdr Output buffer (must be >= HTTP_CAPSULE_HEADER_MAX_SIZE) + * @param hdrlen Size of `hdr` in bytes + * @param payload_len Length of the UDP payload that follows + * @return Number of header bytes written, or 0 on error + * + * @unittest 3400 + */ +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len); +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len) +{ + size_t off = 0; + DEBUGASSERT(hdrlen >= HTTP_CAPSULE_HEADER_MAX_SIZE); + if(hdrlen < HTTP_CAPSULE_HEADER_MAX_SIZE) + return 0; + hdr[off++] = 0; /* capsule type: HTTP Datagram */ + off += capsule_encode_varint_buf(hdr + off, (uint64_t)payload_len + 1); + hdr[off++] = 0; /* context ID */ + return off; +} + +CURLcode Curl_capsule_encap_udp_datagram(struct bufq *q, + const void *buf, size_t blen) +{ + CURLcode result; + uint8_t hdr[HTTP_CAPSULE_HEADER_MAX_SIZE]; + size_t hdr_len, nwritten; + + hdr_len = capsule_encap_udp_hdr(hdr, sizeof(hdr), blen); + DEBUGASSERT(hdr_len); + if(!hdr_len) + return CURLE_FAILED_INIT; + + result = Curl_bufq_write(q, hdr, hdr_len, &nwritten); + if(!result && (nwritten != hdr_len)) + return CURLE_WRITE_ERROR; + if(!result) { + result = Curl_bufq_write(q, buf, blen, &nwritten); + if(!result && (nwritten != blen)) + return CURLE_WRITE_ERROR; + } + if(result == CURLE_AGAIN) + return CURLE_WRITE_ERROR; + return result; +} + +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err) +{ + const unsigned char *context_id, *capsule_type; + size_t read_size, varint_len; + uint64_t capsule_length; + size_t offset, payload_len; + size_t bytes_read = 0; + CURLcode result = CURLE_OK; + + if(!len) { + *err = CURLE_BAD_FUNCTION_ARGUMENT; + return 0; + } + + if(Curl_bufq_is_empty(recvbufq)) { + *err = CURLE_AGAIN; + return 0; + } + + if(!Curl_bufq_peek(recvbufq, &capsule_type, &read_size) || !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(capsule_type[0]) { + infof(data, "Error! Invalid capsule type: %d", capsule_type[0]); + Curl_bufq_skip(recvbufq, 1); + *err = CURLE_RECV_ERROR; + return 0; + } + + offset = 1; + result = capsule_decode_varint_at(recvbufq, offset, &capsule_length, + &varint_len); + if(result == CURLE_AGAIN) { + *err = CURLE_AGAIN; + return 0; + } + else if(result) { + *err = CURLE_RECV_ERROR; + return 0; + } + offset += varint_len; + + if(!Curl_bufq_peek_at(recvbufq, offset, &context_id, &read_size) || + !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(*context_id) { + infof(data, "Error! Invalid context ID: %02x", *context_id); + Curl_bufq_skip(recvbufq, offset + 1); + *err = CURLE_RECV_ERROR; + return 0; + } + offset += 1; + + if(!capsule_length) { + infof(data, "Error! Invalid capsule length: 0"); + Curl_bufq_skip(recvbufq, offset); + *err = CURLE_RECV_ERROR; + return 0; + } + if(capsule_length - 1 >= (uint64_t)SIZE_MAX) { + infof(data, "Error! Capsule length too large: %" CURL_FORMAT_CURL_OFF_T, + (curl_off_t)capsule_length); + *err = CURLE_RECV_ERROR; + return 0; + } + payload_len = (size_t)(capsule_length - 1); + + if(Curl_bufq_len(recvbufq) < offset + payload_len) { + *err = CURLE_AGAIN; + return 0; + } + + if(payload_len > len) { + infof(data, "UDP payload does not fit destination buffer: %zu > %zu", + payload_len, len); + Curl_bufq_skip(recvbufq, offset + payload_len); + *err = CURLE_RECV_ERROR; + return 0; + } + + Curl_bufq_skip(recvbufq, offset); + if(!payload_len) { + *err = CURLE_OK; + return 0; + } + result = Curl_bufq_read(recvbufq, buf, payload_len, &bytes_read); + if(result || (bytes_read != payload_len)) { + infof(data, "Error! Read less than expected %zu %zu", + payload_len, bytes_read); + *err = CURLE_RECV_ERROR; + return 0; + } + + if(cf && data) { + CURL_TRC_CF(data, cf, "Processed UDP capsule raw: size=%zu " + "length_left %zu", payload_len, Curl_bufq_len(recvbufq)); + } + *err = CURLE_OK; + return bytes_read; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/deps/curl/lib/vquic/capsule.h b/deps/curl/lib/vquic/capsule.h new file mode 100644 index 0000000000..7861e6fd73 --- /dev/null +++ b/deps/curl/lib/vquic/capsule.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_CAPSULE_H +#define HEADER_CURL_CAPSULE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "curlx/dynbuf.h" +#include "bufq.h" + +/* HTTP Capsule constants */ +#define HTTP_CAPSULE_HEADER_MAX_SIZE 10 + +/* HTTP Capsule function prototypes */ + +/** + * Encapsulate UDP payload into HTTP Datagram capsule format + * @param q the bufq to write the capsule to + * @param buf Payload buffer + * @param blen Payload buffer length + * @return CURLE_OK on success, error code on failure + */ +CURLcode Curl_capsule_encap_udp_datagram(struct bufq *q, + const void *buf, size_t blen); + +struct Curl_easy; +struct Curl_cfilter; + +/** + * Process one UDP capsule from buffer into raw datagram payload bytes. + * @param cf Connection filter + * @param data Easy handle + * @param recvbufq Buffer queue containing capsule data + * @param buf Output buffer for one datagram payload + * @param len Size of output buffer in bytes + * @param err Error code output + * @return Number of payload bytes written. Check `err` for status. + */ +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err); + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CAPSULE_H */ diff --git a/deps/curl/lib/vquic/cf-capsule.c b/deps/curl/lib/vquic/cf-capsule.c new file mode 100644 index 0000000000..3ff7827bba --- /dev/null +++ b/deps/curl/lib/vquic/cf-capsule.c @@ -0,0 +1,306 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "bufq.h" +#include "select.h" +#include "vquic/capsule.h" +#include "vquic/cf-capsule.h" + +/* send/recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */ +#define CAPSULE_RECV_CHUNKS 4 +#define CAPSULE_SEND_CHUNKS 4 +#define CAPSULE_CHUNK_SIZE (16 * 1024) + +struct cf_capsule_ctx { + struct bufq recvbuf; + struct bufq sendbuf; +}; + +static void cf_capsule_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + Curl_bufq_free(&ctx->sendbuf); + curlx_safefree(ctx); + } +} + +static CURLcode cf_capsule_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + if(cf->next) { + CURLcode result = cf->next->cft->do_connect(cf->next, data, done); + if(!result && *done) + cf->connected = TRUE; + return result; + } + *done = FALSE; + return CURLE_OK; +} + +static CURLcode cf_capsule_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + size_t nwritten; + + if(Curl_bufq_is_empty(&ctx->sendbuf)) + return CURLE_OK; + + result = Curl_cf_send_bufq(cf->next, data, &ctx->sendbuf, NULL, 0, + &nwritten); + if(result) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->sendbuf)); + } + return result; + } + return Curl_bufq_is_empty(&ctx->sendbuf) ? CURLE_OK : CURLE_AGAIN; +} + +static CURLcode cf_capsule_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result; + + (void)eos; + *pnwritten = 0; + + if(Curl_bufq_is_full(&ctx->sendbuf)) { + result = cf_capsule_flush(cf, data); + if(result) + return result; + } + + /* encapsulate new payload into a capsule */ + result = Curl_capsule_encap_udp_datagram(&ctx->sendbuf, buf, len); + if(result) + return result; + + result = cf_capsule_flush(cf, data); + if(result == CURLE_AGAIN) { + /* Could not send it (or all), report success nevertheless as we + * have the payload buffered now and will flush it later. */ + result = CURLE_OK; + } + + if(!result) + *pnwritten = len; + return result; +} + +static CURLcode cf_capsule_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result; + size_t nread; + + *pnread = 0; + + /* fill our receive buffer from the filter below */ + while(!Curl_bufq_is_full(&ctx->recvbuf)) { + result = Curl_cf_recv_bufq(cf->next, data, &ctx->recvbuf, 0, &nread); + if(result == CURLE_AGAIN) + break; + if(result) + return result; + if(!nread) + break; + } + + /* try to extract a complete capsule datagram */ + *pnread = Curl_capsule_process_udp_raw(cf, data, &ctx->recvbuf, + (unsigned char *)buf, len, + &result); + return result; +} + +static bool cf_capsule_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + if(ctx && !Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static CURLcode cf_capsule_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_FLUSH: + result = cf_capsule_flush(cf, data); + break; + default: + break; + } + return result; +} + +static CURLcode cf_capsule_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + (void)pres2; + switch(query) { + case CF_QUERY_NEED_FLUSH: { + if(!Curl_bufq_is_empty(&ctx->sendbuf)) { + *pres1 = TRUE; + return CURLE_OK; + } + break; + } + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static CURLcode cf_capsule_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->sendbuf)) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(sock != CURL_SOCKET_BAD) + return Curl_pollset_add_out(data, ps, sock); + } + return CURLE_OK; +} + +static CURLcode cf_capsule_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + + if(!cf->connected || cf->shutdown) { + *done = TRUE; + } + else { + result = cf_capsule_flush(cf, data); + *done = !result; + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; +} + +struct Curl_cftype Curl_cft_capsule = { + "CAPSULE", + 0, + 0, + cf_capsule_destroy, + cf_capsule_connect, + cf_capsule_shutdown, + cf_capsule_adjust_pollset, + cf_capsule_data_pending, + cf_capsule_send, + cf_capsule_recv, + cf_capsule_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_capsule_query, +}; + +static CURLcode cf_capsule_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = NULL; + struct cf_capsule_ctx *ctx; + CURLcode result; + + (void)data; + (void)conn; + *pcf = NULL; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + Curl_bufq_init2(&ctx->sendbuf, CAPSULE_CHUNK_SIZE, CAPSULE_SEND_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx); + +out: + *pcf = (!result) ? cf : NULL; + if(result && ctx) { + Curl_bufq_free(&ctx->recvbuf); + Curl_bufq_free(&ctx->sendbuf); + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_capsule_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/deps/curl/lib/vquic/cf-capsule.h b/deps/curl/lib/vquic/cf-capsule.h new file mode 100644 index 0000000000..437c9681b6 --- /dev/null +++ b/deps/curl/lib/vquic/cf-capsule.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_CF_CAPSULE_H +#define HEADER_CURL_CF_CAPSULE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +/* Insert a capsule protocol filter after `cf_at` in the filter chain. + * The capsule filter encapsulates/decapsulates UDP datagrams using + * the HTTP Datagram capsule format (RFC 9297). */ +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_capsule; + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CF_CAPSULE_H */ diff --git a/deps/curl/lib/vquic/curl_ngtcp2.c b/deps/curl/lib/vquic/cf-ngtcp2-cmn.c similarity index 52% rename from deps/curl/lib/vquic/curl_ngtcp2.c rename to deps/curl/lib/vquic/cf-ngtcp2-cmn.c index 8cf3886d22..e1ca18cf69 100644 --- a/deps/curl/lib/vquic/curl_ngtcp2.c +++ b/deps/curl/lib/vquic/cf-ngtcp2-cmn.c @@ -24,12 +24,12 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + #include -#include #ifdef USE_OPENSSL #include -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #include #elif defined(OPENSSL_QUIC_API2) #include @@ -45,6 +45,8 @@ #include "vtls/wolfssl.h" #endif +#include + #include "urldata.h" #include "url.h" #include "uint-hash.h" @@ -60,6 +62,7 @@ #include "curlx/dynbuf.h" #include "http1.h" #include "select.h" +#include "sockaddr.h" #include "transfer.h" #include "bufref.h" #include "vquic/vquic.h" @@ -67,34 +70,7 @@ #include "vquic/vquic-tls.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" -#include "vquic/curl_ngtcp2.h" - - -#define QUIC_MAX_STREAMS (256 * 1024) -#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) - -/* We announce a small window size in transport param to the server, - * and grow that immediately to max when no rate limit is in place. - * We need to start small as we are not able to decrease it. */ -#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024) -#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) -#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX) - -#define H3_STREAM_CHUNK_SIZE (64 * 1024) -#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE -#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE -#endif - -/* The pool keeps spares around and half of a full stream windows - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. Memory consumption goes down when streams run empty, - * have a large upload done, etc. */ -#define H3_STREAM_POOL_SPARES 2 -/* The max amount of un-acked upload data we keep around per stream */ -#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#include "vquic/cf-ngtcp2-cmn.h" /* * Store ngtcp2 version info in this buffer. @@ -107,144 +83,7 @@ void Curl_ngtcp2_ver(char *p, size_t len) ng2->version_str, ht3->version_str); } -struct cf_ngtcp2_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; -#ifdef OPENSSL_QUIC_API2 - ngtcp2_crypto_ossl_ctx *ossl_ctx; -#endif - ngtcp2_path connected_path; - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_ccerr last_error; - ngtcp2_crypto_conn_ref conn_ref; - struct cf_call_data call_data; - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - struct curltime started_at; /* time the current attempt started */ - struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ - int qlogfd; - BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ -}; - -/* How to access `call_data` from a cf_ngtcp2 filter */ -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data - -static void h3_stream_hash_free(unsigned int id, void *stream); - -static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - ctx->qlogfd = -1; - ctx->version = NGTCP2_PROTO_VER_MAX; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->initialized = TRUE; -} - -static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - curlx_dyn_free(&ctx->scratch); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - } - curlx_free(ctx); -} - -static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const ngtcp2_transport_params *rp; - /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it closes the - * connection when it does not hear from us for that time. - * - * Some servers use this as a keep-alive timer at a rather low - * value. We are doing HTTP/3 here and waiting for the response - * to a request may take a considerable amount of time. We need - * to prevent the peer's QUIC stack from closing in this case. - */ - if(!ctx->qconn) - return; - - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(!rp || !rp->max_idle_timeout) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); - } - else if(!Curl_uint32_hash_count(&ctx->streams)) { - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); - CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); - } - else { - ngtcp2_duration keep_ns; - keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; - ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); - CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " - "set keep-alive to %" PRIu64 " ms.", - (rp->max_idle_timeout / NGTCP2_MILLISECONDS), - (keep_ns / NGTCP2_MILLISECONDS)); - } -} - -struct pkt_io_ctx; -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - int64_t id; /* HTTP/3 protocol identifier */ - struct bufq sendbuf; /* h3 request body */ - struct h1_req_parser h1; /* h1 request parsing */ - size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - uint64_t rx_offset; /* current receive offset */ - uint64_t rx_offset_max; /* allowed receive offset */ - uint64_t window_size_max; /* max flow control window set for stream */ - int status_code; /* HTTP status code */ - CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ - BIT(resp_hds_complete); /* we have a complete, final response */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is local closed */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ -}; - -static void h3_stream_ctx_free(struct h3_stream_ctx *stream) +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream) { Curl_bufq_free(&stream->sendbuf); Curl_h1_req_parse_free(&stream->h1); @@ -255,165 +94,65 @@ static void h3_stream_hash_free(unsigned int id, void *stream) { (void)id; DEBUGASSERT(stream); - h3_stream_ctx_free((struct h3_stream_ctx *)stream); -} - -static CURLcode h3_data_setup(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(!data) - return CURLE_FAILED_INIT; - - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->id = -1; - stream->rx_offset = 0; - stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; - - /* on send, we control how much we put into the buffer */ - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - stream->sendbuf_len_in_flight = 0; - stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; - Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - - if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - if(Curl_uint32_hash_count(&ctx->streams) == 1) - cf_ngtcp2_setup_keep_alive(cf, data); - - return CURLE_OK; -} - -#if NGTCP2_VERSION_NUM < 0x011100 -struct cf_ngtcp2_sfind_ctx { - int64_t stream_id; - struct h3_stream_ctx *stream; - uint32_t mid; -}; - -static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) -{ - struct cf_ngtcp2_sfind_ctx *fctx = user_data; - struct h3_stream_ctx *stream = value; - - if(fctx->stream_id == stream->id) { - fctx->mid = mid; - fctx->stream = stream; - return FALSE; - } - return TRUE; /* continue */ -} - -static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, - int64_t stream_id) -{ - struct cf_ngtcp2_sfind_ctx fctx; - fctx.stream_id = stream_id; - fctx.stream = NULL; - Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx); - return fctx.stream; + Curl_cf_ngtcp2_h3_stream_ctx_free((struct h3_stream_ctx *)stream); } -#else -static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, - int64_t stream_id) -{ - struct Curl_easy *data = - ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); - - if(!data) { - return NULL; - } - - return H3_STREAM_CTX(ctx, data); -} -#endif -static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) +static bool cf_ngtcp2_h3_err_is_fatal(int code) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - DEBUGASSERT(data); - DEBUGASSERT(stream); - if(!stream->closed && ctx->qconn && ctx->h3conn) { - CURLcode result; - - nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); - ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); - stream->closed = TRUE; - (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, - NGHTTP3_H3_REQUEST_CANCELLED); - result = cf_progress_egress(cf, data, NULL); - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", - stream->id, result); - } + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); } -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)cf; - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - Curl_uint32_hash_remove(&ctx->streams, data->mid); - if(!Curl_uint32_hash_count(&ctx->streams)) - cf_ngtcp2_setup_keep_alive(cf, data); + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); } + if(cf_ngtcp2_h3_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); } -struct pkt_io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - ngtcp2_path_storage ps; -}; - -static void pktx_update_time(struct Curl_easy *data, - struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf) +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + DEBUGASSERT(!ctx->initialized); + ctx->qlogfd = -1; + ctx->tunnel_inbuf = NULL; + ctx->tunnel_inbuf_len = 0; + ctx->version = NGTCP2_PROTO_VER_MAX; + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->init_h3_conn_cb = init_h3_conn_cb; + ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); } -static void pktx_init(struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf, - struct Curl_easy *data) +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - pktx->cf = cf; - pktx->data = data; - ngtcp2_path_storage_zero(&pktx->ps); - vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + if(ctx && ctx->initialized) { + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + curlx_dyn_free(&ctx->scratch); + Curl_uint32_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); + curlx_safefree(ctx->tunnel_inbuf); + ctx->tunnel_inbuf_len = 0; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } } -static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); - static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) { struct Curl_cfilter *cf = conn_ref->user_data; @@ -424,11 +163,8 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) #ifdef DEBUG_NGTCP2 static void quic_printf(void *user_data, const char *fmt, ...) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - (void)ctx; /* need an easy handle to infof() message */ va_list ap; + (void)user_data; va_start(ap, fmt); curl_mvfprintf(stderr, fmt, ap); va_end(ap); @@ -454,7 +190,7 @@ static void qlog_callback(void *user_data, uint32_t flags, static void quic_settings(struct cf_ngtcp2_ctx *ctx, struct Curl_easy *data, - struct pkt_io_ctx *pktx) + struct cf_ngtcp2_io_ctx *pktx) { ngtcp2_settings *s = &ctx->settings; ngtcp2_transport_params *t = &ctx->transport_params; @@ -490,10 +226,12 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, } } -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data); +#if defined(_MSC_VER) && defined(_DLL) +#pragma warning(push) +#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif -static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) +static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; @@ -504,24 +242,26 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); if(!ctx || !data) - return NGHTTP3_ERR_CALLBACK_FAILURE; + return NGTCP2_ERR_CALLBACK_FAILURE; ctx->handshake_at = *Curl_pgrs_now(data); ctx->tls_handshake_complete = TRUE; Curl_vquic_report_handshake(&ctx->tls, cf, data); ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, - data, &ctx->peer); + data, &ctx->ssl_peer); + if(ctx->tls_vrfy_result) + return NGTCP2_ERR_CALLBACK_FAILURE; + #ifdef CURLVERBOSE if(Curl_trc_is_verbose(data)) { const ngtcp2_transport_params *rp; rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms, remote transport[max_udp_payload=%" PRIu64 - ", initial_max_data=%" PRIu64 - "]", - curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), - rp->max_udp_payload_size, rp->initial_max_data); + ", initial_max_data=%" PRIu64 "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); } #endif @@ -558,58 +298,12 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) return 0; } -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static bool cf_ngtcp2_err_is_fatal(int code) -{ - return (NGTCP2_ERR_FATAL >= code) || - (NGTCP2_ERR_DROP_CONN == code) || - (NGTCP2_ERR_IDLE_CLOSE == code); -} - -static void cf_ngtcp2_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - if(NGTCP2_ERR_CRYPTO == code) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); - } - } - if(cf_ngtcp2_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static bool cf_ngtcp2_h3_err_is_fatal(int code) -{ - return (NGHTTP3_ERR_FATAL >= code) || - (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); -} - -static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - ngtcp2_ccerr_set_application_error(&ctx->last_error, - nghttp3_err_infer_quic_app_error_code(code), NULL, 0); - } - if(cf_ngtcp2_h3_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; + struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; nghttp3_ssize rc; uint64_t nconsumed; @@ -687,7 +381,7 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" PRIu64 ") -> %d", stream_id, app_error_code, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - cf_ngtcp2_h3_err_set(cf, data, rv); + Curl_cf_ngtcp2_h3_err_set(cf, data, rv); return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -811,7 +505,8 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, } #ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ -static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid, +static int cb_get_new_connection_id2( + ngtcp2_conn *tconn, ngtcp2_cid *cid, struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) { CURLcode result; @@ -844,23 +539,18 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, DEBUGASSERT(ctx); DEBUGASSERT(data); - if(ctx && data && !ctx->h3conn) { - if(init_ngh3_conn(cf, data)) + if(ctx && data && !ctx->h3conn && ctx->init_h3_conn_cb) { + if(ctx->init_h3_conn_cb(cf, data, ctx)) return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } -#if defined(_MSC_VER) && defined(_DLL) -#pragma warning(push) -#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ -#endif - static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ ngtcp2_crypto_recv_crypto_data_cb, - cf_ngtcp2_handshake_completed, + cb_ngtcp2_handshake_completed, NULL, /* recv_version_negotiation */ ngtcp2_crypto_encrypt_cb, ngtcp2_crypto_decrypt_cb, @@ -912,994 +602,756 @@ static ngtcp2_callbacks ng_callbacks = { #pragma warning(pop) #endif -/** - * Connection maintenance like timeouts on packet ACKs etc. are done by us, not - * the OS like for TCP. POLL events on the socket therefore are not - * sufficient. - * ngtcp2 tells us when it wants to be invoked again. We handle that via - * the `Curl_expire()` mechanisms. - */ -static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - ngtcp2_tstamp expiry; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx_update_time(data, pktx, cf); - } - - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= pktx->ts) { - CURLcode result; - int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - cf_ngtcp2_err_set(cf, data, rv); - return CURLE_SEND_ERROR; - } - result = cf_progress_ingress(cf, data, pktx); - if(result) - return result; - result = cf_progress_egress(cf, data, pktx); - if(result) - return result; - /* ask again, things might have changed */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - } - - if(expiry > pktx->ts) { - ngtcp2_duration timeout = expiry - pktx->ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), - EXPIRE_QUIC); - } - } - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - bool want_recv, want_send; - CURLcode result = CURLE_OK; - - if(!ctx->qconn) - return CURLE_OK; - - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); - if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) - want_send = TRUE; - - if(want_recv || want_send) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - bool c_exhaust, s_exhaust; - - CF_DATA_SAVE(save, cf, data); - c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || - !ngtcp2_conn_get_max_data_left(ctx->qconn)); - s_exhaust = want_send && stream && stream->id >= 0 && - stream->quic_flow_blocked; - want_recv = (want_recv || c_exhaust || s_exhaust); - want_send = (!s_exhaust && want_send) || - !Curl_bufq_is_empty(&ctx->q.sendbuf); - - result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); - CF_DATA_RESTORE(cf, save); - } - return result; +#ifdef USE_OPENSSL + return Curl_ossl_need_httpsrr(data); +#elif defined(USE_WOLFSSL) + return Curl_wssl_need_httpsrr(data); +#else + (void)data; + return FALSE; +#endif } -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) +#ifdef USE_OPENSSL +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)stream_id; + struct Curl_cfilter *cf; + struct cf_ngtcp2_ctx *ctx; + struct Curl_easy *data; + ngtcp2_crypto_conn_ref *cref; - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) - return 0; + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref ? cref->user_data : NULL; + ctx = cf ? cf->ctx : NULL; + data = cf ? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; +#ifdef HAVE_OPENSSL_EARLYDATA + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; - stream->closed = TRUE; - stream->error3 = app_error_code; - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - stream->reset = TRUE; - stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, - stream->id, stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } +#endif + Curl_ossl_add_session(cf, data, ctx->ssl_peer.scache_key, ssl_sessionid, + SSL_version(ssl), "h3", quic_tp, quic_tp_len); } - Curl_multi_mark_dirty(data); return 0; } +#endif /* USE_OPENSSL */ -static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) -{ - /* This function returns no error intentionally, but records - * the result at the stream, skipping further writes once the - * `result` of the transfer is known. - * The stream is subsequently cancelled "higher up" in the filter's - * send/recv callbacks. Closing the stream here leads to SEND/RECV - * errors in other places that then overwrite the transfer's result. */ - if(!stream->xfer_result) { - stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); - if(stream->xfer_result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, blen); - } -} +#ifdef USE_GNUTLS -static void h3_xfer_write_resp(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) +#ifdef CURLVERBOSE +static const char *gtls_hs_msg_name(int mtype) { - /* This function returns no error intentionally, but records - * the result at the stream, skipping further writes once the - * `result` of the transfer is known. - * The stream is subsequently cancelled "higher up" in the filter's - * send/recv callbacks. Closing the stream here leads to SEND/RECV - * errors in other places that then overwrite the transfer's result. */ - if(!stream->xfer_result) { - stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); - /* If the transfer write is errored, we do not want any more data */ - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", - stream->id, stream->xfer_result, blen); - } + switch(mtype) { + case 1: + return "ClientHello"; + case 2: + return "ServerHello"; + case 4: + return "SessionTicket"; + case 8: + return "EncryptedExtensions"; + case 11: + return "Certificate"; + case 13: + return "CertificateRequest"; + case 15: + return "CertificateVerify"; + case 20: + return "Finished"; + case 24: + return "KeyUpdate"; + case 254: + return "MessageHash"; } + return "Unknown"; } +#endif -static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) +static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, + const gnutls_datum_t *msg) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; - - /* how much does rate limiting allow us to acknowledge? */ - if(Curl_rlimit_active(&data->progress.dl.rlimit)) { - int64_t avail; - - /* start rate limit updates only after first bytes arrived */ - if(!stream->rx_offset) - return; - - avail = Curl_rlimit_avail(&data->progress.dl.rlimit, - Curl_pgrs_now(data)); - if(avail <= 0) { - /* nothing available, do not extend the rx offset */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 - " tokens)", stream->id, avail); - return; - } - wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); - } - - if(stream->rx_offset_max < stream->rx_offset) { - DEBUGASSERT(0); - return; - } - cur_win = stream->rx_offset_max - stream->rx_offset; + ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - if(wanted_win > cur_win) { - uint64_t delta = wanted_win - cur_win; + (void)msg; + (void)incoming; + if(when && cf && ctx) { /* after message has been processed */ + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!data) + return 0; + CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", + incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; - if(UINT64_MAX - delta < stream->rx_offset_max) - delta = UINT64_MAX - stream->rx_offset_max; - if(delta) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] rx window, extend by %" PRIu64 - " bytes", stream->id, delta); - stream->rx_offset_max += delta; - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, delta); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_gtls_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, 0, "h3", quic_tp, quic_tp_len); + break; + } + default: + break; } } + return 0; } +#endif /* USE_GNUTLS */ -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t blen, - void *user_data, void *stream_user_data) +#ifdef USE_WOLFSSL +static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - (void)conn; - (void)stream3_id; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - - h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE); + ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - ngtcp2_conn_extend_max_offset(ctx->qconn, blen); - stream->rx_offset += blen; - if(stream->rx_offset_max < stream->rx_offset) - stream->rx_offset_max = stream->rx_offset; + DEBUGASSERT(cf); + if(cf && session) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(data && ctx) { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, - stream->id, blen, stream->rx_offset_max - stream->rx_offset); - cf_ngtcp2_upd_rx_win(cf, data, stream); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_wssl_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, wolfSSL_version(ssl), + "h3", quic_tp, quic_tp_len); + } + } return 0; } +#endif /* USE_WOLFSSL */ -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, - size_t consumed, void *user_data, - void *stream_user_data) +static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; + struct curl_tls_ctx *ctx = user_data; - /* nghttp3 has consumed bytes on the QUIC stream and we need to - * tell the QUIC connection to increase its flow control */ - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); - ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); - if(stream) { - stream->rx_offset += consumed; - stream->rx_offset_max += consumed; +#ifdef USE_OPENSSL +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#elif defined(OPENSSL_QUIC_API2) + /* nothing to do */ +#else + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ + if(Curl_ssl_scache_use(cf, data)) { + /* Enable the session cache because it is a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); } - 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 cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)conn; - (void)stream_id; - (void)fin; - (void)cf; - if(!stream) - return 0; - /* add a CRLF only if we have received some headers */ - h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), - (bool)stream->closed); +#elif defined(USE_GNUTLS) + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { + failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); + return CURLE_FAILED_INIT; + } + if(Curl_ssl_scache_use(cf, data)) { + gnutls_handshake_set_hook_function(ctx->gtls.session, + GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, + quic_gtls_handshake_cb); + } - 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; +#elif defined(USE_WOLFSSL) + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + return CURLE_FAILED_INIT; } - Curl_multi_mark_dirty(data); - return 0; + if(Curl_ssl_scache_use(cf, data)) { + /* Register to get notified when a new session is received */ + wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb); + } +#endif + return CURLE_OK; } -static 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) +static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data) { - struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(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) { - - result = Curl_http_decode_status(&stream->status_code, - (const char *)h3val.base, h3val.len); - if(result) - return NGHTTP3_ERR_CALLBACK_FAILURE; - curlx_dyn_reset(&ctx->scratch); - result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3val.base, h3val.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); - if(!result) - h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), - curlx_dyn_len(&ctx->scratch), FALSE); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", - stream_id, curlx_dyn_ptr(&ctx->scratch)); - if(result) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } + *do_early_data = FALSE; +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_max = scs->earlydata_max; +#endif +#ifdef USE_GNUTLS + ctx->earlydata_max = + gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); +#endif +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_max = scs->earlydata_max; +#else + ctx->earlydata_max = 0; +#endif /* WOLFSSL_EARLY_DATA */ +#endif +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if(!ctx->earlydata_max) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } - else { - /* 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); - curlx_dyn_reset(&ctx->scratch); - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3name.base, h3name.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST(": ")); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3val.base, h3val.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); - if(!result) - h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), - curlx_dyn_len(&ctx->scratch), FALSE); + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early 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 cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, - app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; + else if(!scs->quic_tp || !scs->quic_tp_len) { + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); } - - return 0; + else { + int rv; + rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( + ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); + if(rv) + CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " + "parameters: %s", ngtcp2_strerror(rv)); + else if(ctx->init_h3_conn_cb) { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); + result = ctx->init_h3_conn_cb(cf, data, ctx); + if(!result) { + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } + else { /* h3_conn_init set, assume done */ + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } +#else /* not supported in the TLS backend */ + (void)data; + (void)ctx; + (void)scs; + (void)alpns; +#endif + return result; } -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) +/* + * Might be called twice for happy eyeballs. + */ +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { - struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; + int rc; int rv; - (void)conn; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr = NULL; + int qfd; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, - app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } + DEBUGASSERT(ctx->initialized); + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; - return 0; -} + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_req_body, /* 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 (deprecated) */ -#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ - NULL, /* recv_origin */ - NULL, /* end_origin */ - NULL, /* rand */ -#endif -#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ - NULL, /* recv_settings2 */ -#endif -}; + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + quic_settings(ctx, data, pktx); -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; - int rc; + result = vquic_ctx_init(data, &ctx->q); + if(result) + return result; - if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { - failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); - return CURLE_QUIC_CONNECT_ERROR; + /* Query socket and remote address from sub-chain */ + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) { + /* No direct socket - must be tunneled QUIC (CONNECT-UDP through proxy) */ + ctx->q.sockfd = CURL_SOCKET_BAD; + } + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get local address for ngtcp2 */ + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); + rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; } + else { + /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address + from the connected filter below */ + const struct Curl_sockaddr_ex *remote = NULL; + if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&remote))) + return CURLE_QUIC_CONNECT_ERROR; + if(!remote) + return CURLE_QUIC_CONNECT_ERROR; + + memset(&ctx->q.local_addr, 0, sizeof(ctx->q.local_addr)); + switch(remote->family) { + case AF_INET: + ((struct sockaddr_in *)&ctx->q.local_addr)->sin_family = AF_INET; + ctx->q.local_addrlen = sizeof(struct sockaddr_in); + break; +#ifdef USE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)&ctx->q.local_addr)->sin6_family = AF_INET6; + ctx->q.local_addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return CURLE_QUIC_CONNECT_ERROR; + } - nghttp3_settings_default(&ctx->h3settings); - - rc = nghttp3_conn_client_new(&ctx->h3conn, - &ngh3_callbacks, - &ctx->h3settings, - Curl_nghttp3_mem(), - cf); - if(rc) { - failf(data, "error creating nghttp3 connection instance"); - return CURLE_OUT_OF_MEMORY; - } + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &remote->curl_sa_addr, + (socklen_t)remote->addrlen); - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; - rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; } - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack encoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } + result = Curl_vquic_tls_init(&ctx->tls, cf, data, + &ctx->ssl_peer, &ALPN_SPEC_H3, + cf_ngtcp2_tls_ctx_setup, &ctx->tls, + &ctx->conn_ref, + cf_ngtcp2_on_session_reuse); + if(result) + return result; - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack decoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; +#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) + if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); + return CURLE_FAILED_INIT; } - - rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); + if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); + return CURLE_FAILED_INIT; } +#elif defined(USE_OPENSSL) + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); +#elif defined(USE_WOLFSSL) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); +#else +#error "ngtcp2 TLS backend not defined" +#endif - return CURLE_OK; -} + ngtcp2_ccerr_default(&ctx->last_error); -static CURLcode recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - size_t *pnread) -{ - (void)cf; - *pnread = 0; - if(stream->reset) { - if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { - infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " - "on a new connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ - data->state.refused_stream = TRUE; - return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - } - else if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " - "but we did not want a body anyway, ignore error 0x%" - PRIx64 " %s", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); - return CURLE_OK; - } - failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 - " %s)", stream->id, stream->error3, - vquic_h3_err_str(stream->error3)); - return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; - } - else if(!stream->resp_hds_complete) { - failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before " - "getting all response header fields, treated as error", - stream->id); - return CURLE_HTTP3; - } return CURLE_OK; } -/* incoming data frames on the h3 stream */ -static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t blen, size_t *pnread) +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - struct pkt_io_ctx pktx; CURLcode result = CURLE_OK; - int i; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; - (void)ctx; - (void)buf; - NOVERBOSE((void)blen); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - *pnread = 0; - - /* handshake verification failed in callback, do not recv anything */ - if(ctx->tls_vrfy_result) { - result = ctx->tls_vrfy_result; - goto denied; + /* Connect the sub-chain */ + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; } - pktx_init(&pktx, cf, data); + *done = FALSE; - if(!stream || ctx->shutdown_started) { - result = CURLE_RECV_ERROR; - goto out; + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; } - cf_ngtcp2_upd_rx_win(cf, data, stream); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + CF_DATA_SAVE(save, cf, data); - /* first check for results/closed already known without touching - * the connection. For an already failed/closed stream, errors on - * the connection do not count. - * Then handle incoming data and check for failed/closed again. - */ - for(i = 0; i < 2; ++i) { - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - result = recv_closed_stream(cf, data, stream, pnread); + if(!ctx->qconn) { + ctx->started_at = *Curl_pgrs_now(data); + result = cf_connect_start(cf, data, &pktx); + if(result) goto out; - } - - if(!i && cf_progress_ingress(cf, data, &pktx)) { - result = CURLE_RECV_ERROR; + if(cf->connected) { + *done = TRUE; goto out; } + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + /* we do not expect to be able to recv anything yet */ + goto out; } - result = CURLE_AGAIN; - -out: - result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); - result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); -denied: - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", - stream ? stream->id : -1, blen, result, *pnread); - CF_DATA_RESTORE(cf, save); - return result; -} + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; -static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - size_t skiplen; + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(result) + goto out; - (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; - - /* Resume upload processing if we have more data to send */ - if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; + if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { + result = ctx->tls_vrfy_result; + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + cf->connected = TRUE; + *done = TRUE; } } - 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 cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nwritten = 0; - size_t nvecs = 0; - (void)cf; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; +out: + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; + if(ctx->qconn && + ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && + ngtcp2_conn_in_draining_period(ctx->qconn)) { + const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); - 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, - CURL_UNCONST(&vec[nvecs].base), - &vec[nvecs].len)) { - stream->sendbuf_len_in_flight += vec[nvecs].len; - nwritten += vec[nvecs].len; - ++nvecs; + result = CURLE_COULDNT_CONNECT; + if(cerr) { + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, + (int)cerr->type, cerr->error_code); + switch(cerr->type) { + case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: + CURL_TRC_CF(data, cf, "error in version negotiation"); + break; + default: + if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { + CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", + (unsigned int)(cerr->error_code & 0xffU)); + } + else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { + CURL_TRC_CF(data, cf, "connection refused by server"); + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE with this code right away. We want + * to keep on trying in this case. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + } } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ } - if(nwritten > 0 && stream->upload_left != -1) - stream->upload_left -= nwritten; +#ifdef CURLVERBOSE + if(result) { + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get IP info for error reporting */ + struct ip_quadruple ip; - /* 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; + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + infof(data, "QUIC connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + } } - else if(!nwritten) { - /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", stream->id); - return NGHTTP3_ERR_WOULDBLOCK; +#endif + if(!result && ctx->qconn) { + result = Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx); } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", - stream->id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left); - return (nghttp3_ssize)nvecs; + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); + CF_DATA_RESTORE(cf, save); + return result; } -static CURLcode h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const uint8_t *buf, size_t len, - size_t *pnwritten) +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - int64_t sid; - struct dynhds h2_headers; - size_t nheader; - nghttp3_nv *nva = NULL; - int rc = 0; - unsigned int i; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - CURLcode result; - - *pnwritten = 0; - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; - result = h3_data_setup(cf, data); - if(result) - goto out; - stream = H3_STREAM_CTX(ctx, data); - DEBUGASSERT(stream); - if(!stream) { - result = CURLE_FAILED_INIT; - goto out; + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; } - result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); - if(result) - goto out; - if(!stream->h1.done) { - /* need more data */ - goto out; + if(!cf->next) { + Curl_bufq_reset(&ctx->q.sendbuf); + *done = TRUE; + return CURLE_OK; } - DEBUGASSERT(stream->h1.req); - result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(result) - goto out; + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); - /* no longer needed */ - Curl_h1_req_parse_free(&stream->h1); + if(!ctx->shutdown_started) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_ssize nwritten; - nheader = Curl_dynhds_count(&h2_headers); - nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); + *done = TRUE; + 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(Curl_bufq_is_empty(&ctx->q.sendbuf)); + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + PRIu64 ") -> %zd", (int)ctx->last_error.type, + ctx->last_error.error_code, (ssize_t)nwritten); + /* there are cases listed in ngtcp2 documentation where this call + * may fail. Since we are doing a connection shutdown as graceful + * as we can, such an error is ignored here. */ + if(nwritten > 0) { + /* Ignore amount written. sendbuf was empty and has always room for + * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which + * case `result` is set non zero. */ + size_t n; + result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &n); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", (int)result); + goto out; + } - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); - if(rc) { - failf(data, "can get bidi streams"); - result = CURLE_SEND_ERROR; - goto out; - } - stream->id = sid; - ++ctx->used_bidi_streams; - - 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->h3conn, stream->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->id); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> " - "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); - break; + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; } - cf_ngtcp2_stream_close(cf, data, stream); - result = CURLE_SEND_ERROR; - goto out; } - cf_ngtcp2_upd_rx_win(cf, data, stream); - - if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", - stream->id, Curl_bufref_ptr(&data->state.url)); - for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); + *done = TRUE; + goto out; } } + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. We are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; + } out: - curlx_free(nva); - Curl_dynhds_free(&h2_headers); + CF_DATA_RESTORE(cf, save); return result; } -static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - struct pkt_io_ctx pktx; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - pktx_init(&pktx, cf, data); - *pnwritten = 0; - - /* handshake verification failed in callback, do not send anything */ - if(ctx->tls_vrfy_result) { - result = ctx->tls_vrfy_result; - goto denied; - } + bool done; + Curl_cf_ngtcp2_cmn_shutdown(cf, data, &done); +} - (void)eos; /* use for stream EOF and block handling */ - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; +static bool cf_ngtcp2_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} - if(!stream || stream->id < 0) { - if(ctx->shutdown_started) { - CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); - result = CURLE_SEND_ERROR; - goto out; - } - result = h3_stream_open(cf, data, buf, len, pnwritten); - if(result) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); - goto out; +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); } - VERBOSE(stream = H3_STREAM_CTX(ctx, data)); - } - else if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - if(stream->resp_hds_complete) { - /* Server decided to close the stream after having sent us a final - * response. This is valid if it is not interested in the request - * body. This happens on 30x or 40x responses. - * We silently discard the data sent, since this is not a transport - * error situation. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" - "on closed stream with response", stream->id); - result = CURLE_OK; - *pnwritten = len; - goto out; + else { + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " - "-> stream closed", stream->id, len); - result = CURLE_HTTP3; - goto out; - } - else if(ctx->shutdown_started) { - CURL_TRC_CF(data, cf, "cannot send on closed connection"); - result = CURLE_SEND_ERROR; - goto out; - } - else { - result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %d, %zu", - stream->id, len, result, *pnwritten); - if(result) - goto out; - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } + if(cf_ngtcp2_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); +} - if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) - ctx->earlydata_skip += *pnwritten; +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); - DEBUGASSERT(!result); - result = cf_progress_egress(cf, data, &pktx); + io_ctx->cf = cf; + io_ctx->data = data; + ngtcp2_path_storage_zero(&io_ctx->ps); + vquic_ctx_set_time(&ctx->q, pnow); + io_ctx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} -out: - result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); -denied: - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->id : -1, len, result, *pnwritten); - CF_DATA_RESTORE(cf, save); - return result; +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx, + struct Curl_cfilter *cf) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); + + vquic_ctx_update_time(&ctx->q, pnow); + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } -struct cf_ngtcp2_recv_ctx { - struct pkt_io_ctx *pktx; - size_t pkt_count; +#if NGTCP2_VERSION_NUM < 0x011100 +struct cf_ngtcp2_sfind_ctx { + int64_t stream_id; + struct h3_stream_ctx *stream; + uint32_t mid; }; -static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, - size_t gso_size, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) +static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) { - struct cf_ngtcp2_recv_ctx *rctx = userp; - struct pkt_io_ctx *pktx = rctx->pktx; - struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; - ngtcp2_pkt_info pi; - ngtcp2_path path; - size_t offset, pktlen; - int rv; - - if(!rctx->pkt_count) { - pktx_update_time(pktx->data, pktx, pktx->cf); - ngtcp2_path_storage_zero(&pktx->ps); - } - - if(ecn) - CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", - buflen, gso_size, ecn); - ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, - remote_addrlen); - pi.ecn = (uint8_t)ecn; - - for(offset = 0; offset < buflen; offset += gso_size) { - rctx->pkt_count++; - pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, - buf + offset, pktlen, pktx->ts); - if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", - ngtcp2_strerror(rv), rv); - cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); + struct cf_ngtcp2_sfind_ctx *fctx = user_data; + struct h3_stream_ctx *stream = value; - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } + if(fctx->stream_id == stream->id) { + fctx->mid = mid; + fctx->stream = stream; + return FALSE; } - return CURLE_OK; + return TRUE; /* continue */ } -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - struct cf_ngtcp2_recv_ctx rctx; - CURLcode result = CURLE_OK; + struct cf_ngtcp2_sfind_ctx fctx; + fctx.stream_id = stream_id; + fctx.stream = NULL; + Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx); + return fctx.stream; +} +#else +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) +{ + struct Curl_easy *data = + ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; + if(!data) { + return NULL; } - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - return result; - - rctx.pktx = pktx; - rctx.pkt_count = 0; - return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts, &rctx); + return H3_STREAM_CTX(ctx, data); } +#endif /** * Read a network packet to send from ngtcp2 into `buf`. @@ -1909,7 +1361,7 @@ static CURLcode read_pkt_to_send(void *userp, unsigned char *buf, size_t buflen, size_t *pnread) { - struct pkt_io_ctx *x = userp; + struct cf_ngtcp2_io_ctx *x = userp; struct cf_ngtcp2_ctx *ctx = x->cf->ctx; nghttp3_vec vec[16]; nghttp3_ssize veccnt; @@ -1939,7 +1391,7 @@ static CURLcode read_pkt_to_send(void *userp, if(veccnt < 0) { failf(x->data, "nghttp3_conn_writev_stream returned error: %s", nghttp3_strerror((int)veccnt)); - cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); + Curl_cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); return CURLE_SEND_ERROR; } } @@ -1983,7 +1435,7 @@ static CURLcode read_pkt_to_send(void *userp, DEBUGASSERT(ndatalen == -1); failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", ngtcp2_strerror((int)n)); - cf_ngtcp2_err_set(x->cf, x->data, (int)n); + Curl_cf_ngtcp2_cmn_err_set(x->cf, x->data, (int)n); return CURLE_SEND_ERROR; } } @@ -2006,9 +1458,9 @@ static CURLcode read_pkt_to_send(void *userp, } } -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; size_t nread; @@ -2016,25 +1468,25 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, size_t pktcnt = 0; size_t gsolen = 0; /* this disables gso until we have a clue */ size_t send_quantum; - CURLcode curlcode; - struct pkt_io_ctx local_pktx; + CURLcode result; + struct cf_ngtcp2_io_ctx local_pktx; if(!pktx) { - pktx_init(&local_pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); pktx = &local_pktx; } else { - pktx_update_time(data, pktx, cf); + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); ngtcp2_path_storage_zero(&pktx->ps); } - curlcode = vquic_flush(cf, data, &ctx->q); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } /* In UDP, there is a maximum theoretical packet payload length and @@ -2056,12 +1508,12 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, send_quantum); for(;;) { /* add the next packet to send, if any, to our buffer */ - curlcode = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, pktx, &nread); - if(curlcode == CURLE_AGAIN) + result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &nread); + if(result == CURLE_AGAIN) break; - else if(curlcode) - return curlcode; + else if(result) + return result; else { size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); if((buflen >= send_quantum) || @@ -2079,14 +1531,14 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* The added packet is a PMTUD *or* the one(s) before the * added were PMTUD and the last one is smaller. * Flush the buffer before the last add. */ - curlcode = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } pktcnt = 0; } @@ -2102,797 +1554,334 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* time to send */ CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } - pktx_update_time(data, pktx, cf); + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); } return CURLE_OK; } -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams - * windows. As we do in HTTP/2. */ - (void)cf; - if(!pause) - Curl_multi_mark_dirty(data); - return CURLE_OK; -} +struct cf_ngtcp2_recv_ctx { + struct cf_ngtcp2_io_ctx *pktx; + size_t pkt_count; +}; -static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) +static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; + struct cf_ngtcp2_recv_ctx *rctx = userp; + struct cf_ngtcp2_io_ctx *pktx = rctx->pktx; + struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; + ngtcp2_pkt_info pi; + ngtcp2_path path; + size_t offset, pktlen; + int rv; - 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_DONE: - h3_data_done(cf, data); - break; - case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(stream && !stream->send_closed) { - stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf) - - stream->sendbuf_len_in_flight; - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) { - cf->conn->httpversion_seen = 30; - Curl_conn_set_multiplex(cf->conn); - } - break; - default: - break; + if(!rctx->pkt_count) { + Curl_cf_ngtcp2_io_ctx_update_time(pktx->data, pktx, pktx->cf); + ngtcp2_path_storage_zero(&pktx->ps); } - CF_DATA_RESTORE(cf, save); - return result; -} -static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; + if(ecn) + CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", + buflen, gso_size, (unsigned int)ecn); + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, + remote_addrlen); + pi.ecn = (uint8_t)ecn; - if(!ctx->initialized) - return; - if(ctx->qlogfd != -1) { - curlx_close(ctx->qlogfd); - } - ctx->qlogfd = -1; - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - if(ctx->h3conn) { - nghttp3_conn_del(ctx->h3conn); - ctx->h3conn = NULL; - } - if(ctx->qconn) { - ngtcp2_conn_del(ctx->qconn); - ctx->qconn = NULL; - } -#ifdef OPENSSL_QUIC_API2 - if(ctx->ossl_ctx) { - ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); - ctx->ossl_ctx = NULL; + for(offset = 0; offset < buflen; offset += gso_size) { + rctx->pkt_count++; + pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, + buf + offset, pktlen, pktx->ts); + if(rv) { + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); + Curl_cf_ngtcp2_cmn_err_set(pktx->cf, pktx->data, rv); + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } } -#endif - ctx->call_data = save; + return CURLE_OK; } -static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - struct pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx local_pktx; + struct cf_ngtcp2_recv_ctx rctx; CURLcode result = CURLE_OK; - if(cf->shutdown || !ctx->qconn) { - *done = TRUE; - return CURLE_OK; + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; } - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - pktx_init(&pktx, cf, data); + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; - if(!ctx->shutdown_started) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_ssize nwritten; + rctx.pktx = pktx; + rctx.pkt_count = 0; - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); - result = cf_progress_egress(cf, data, &pktx); - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + CURL_TRC_CF(data, cf, "progress_ingress(socket)"); + return vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts, &rctx); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + unsigned char *buf; + size_t max_udp_payload = QUIC_TUNNEL_INBUF_SIZE; + size_t pkt_limit = QUIC_TUNNEL_INGRESS_PKT_LIMIT; + size_t nread; + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + + CURL_TRC_CF(data, cf, "progress_ingress(sub-filters)"); + if(ctx->qconn) { + size_t max_path_payload; + max_path_payload = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + if(max_path_payload > max_udp_payload) + max_udp_payload = max_path_payload; } - DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); - ctx->shutdown_started = TRUE; - nwritten = ngtcp2_conn_write_connection_close( - ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %zd", ctx->last_error.type, - ctx->last_error.error_code, (ssize_t)nwritten); - /* there are cases listed in ngtcp2 documentation where this call - * may fail. Since we are doing a connection shutdown as graceful - * as we can, such an error is ignored here. */ - if(nwritten > 0) { - /* Ignore amount written. sendbuf was empty and has always room for - * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which - * case `result` is set non zero. */ - size_t n; - result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, - (size_t)nwritten, &n); + if(ctx->tunnel_inbuf_len < max_udp_payload) { + unsigned char *newbuf = curlx_realloc(ctx->tunnel_inbuf, + max_udp_payload); + if(!newbuf) + return CURLE_OUT_OF_MEMORY; + ctx->tunnel_inbuf = newbuf; + ctx->tunnel_inbuf_len = max_udp_payload; + } + buf = ctx->tunnel_inbuf; + + while(pkt_limit--) { + result = Curl_conn_cf_recv(cf->next, data, (char *)buf, + ctx->tunnel_inbuf_len, &nread); + if(result == CURLE_AGAIN) { + /* no more data available at the moment */ + return CURLE_OK; + } if(result) { - CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " - "aborting shutdown", result); - goto out; + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", + (int)result); + return result; + } + if(nread == 0) { + /* tunnel closed */ + return CURLE_OK; } - ctx->q.no_gso = TRUE; - ctx->q.gsolen = (size_t)nwritten; - ctx->q.split_len = 0; - } - } + memcpy(&remote_addr, ctx->connected_path.remote.addr, + ctx->connected_path.remote.addrlen); + remote_addrlen = (socklen_t)ctx->connected_path.remote.addrlen; + result = cf_ngtcp2_recv_pkts(buf, nread, nread, &remote_addr, + remote_addrlen, 0, &rctx); + if(result) + return result; - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing egress"); - result = vquic_flush(cf, data, &ctx->q); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; + if(!ctx->q.got_first_byte) { + ctx->q.got_first_byte = TRUE; + ctx->q.first_byte_at = ctx->q.last_op; + } + ctx->q.last_io = ctx->q.last_op; } + return CURLE_OK; } - - if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. We are done. */ - CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); - *done = TRUE; - result = CURLE_OK; - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - bool done; - cf_ngtcp2_shutdown(cf, data, &done); } -static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +/** + * Connection maintenance like timeouts on packet ACKs etc. are done by us, not + * the OS like for TCP. POLL events on the socket therefore are not + * sufficient. + * ngtcp2 tells us when it wants to be invoked again. We handle that via + * the `Curl_expire()` mechanisms. + */ +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->qconn) { - cf_ngtcp2_conn_close(cf, data); - cf_ngtcp2_ctx_close(ctx); - CURL_TRC_CF(data, cf, "close"); - } - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} + struct cf_ngtcp2_io_ctx local_pktx; + ngtcp2_tstamp expiry; -static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "destroy"); - if(cf->ctx) { - cf_ngtcp2_close(cf, data); - cf_ngtcp2_ctx_free(cf->ctx); - cf->ctx = NULL; - } -} - -#ifdef USE_OPENSSL -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) -{ - struct Curl_cfilter *cf; - struct cf_ngtcp2_ctx *ctx; - struct Curl_easy *data; - ngtcp2_crypto_conn_ref *cref; - - cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); - cf = cref ? cref->user_data : NULL; - ctx = cf ? cf->ctx : NULL; - data = cf ? CF_DATA_CURRENT(cf) : NULL; - if(cf && data && ctx) { - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; -#ifdef HAVE_OPENSSL_EARLYDATA - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } -#endif - Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, - SSL_version(ssl), "h3", quic_tp, quic_tp_len); - } - return 0; -} -#endif /* USE_OPENSSL */ - -#ifdef USE_GNUTLS - -#ifdef CURLVERBOSE -static const char *gtls_hs_msg_name(int mtype) -{ - switch(mtype) { - case 1: - return "ClientHello"; - case 2: - return "ServerHello"; - case 4: - return "SessionTicket"; - case 8: - return "EncryptedExtensions"; - case 11: - return "Certificate"; - case 13: - return "CertificateRequest"; - case 15: - return "CertificateVerify"; - case 20: - return "Finished"; - case 24: - return "KeyUpdate"; - case 254: - return "MessageHash"; - } - return "Unknown"; -} -#endif - -static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, - unsigned when, unsigned int incoming, - const gnutls_datum_t *msg) -{ - ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - - (void)msg; - (void)incoming; - if(when && cf && ctx) { /* after message has been processed */ - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(!data) - return 0; - CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", - incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); - switch(htype) { - case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key, - session, 0, "h3", quic_tp, quic_tp_len); - break; - } - default: - break; - } - } - return 0; -} -#endif /* USE_GNUTLS */ - -#ifdef USE_WOLFSSL -static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) -{ - ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - - DEBUGASSERT(cf != NULL); - if(cf && session) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(data && ctx) { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key, - session, wolfSSL_version(ssl), - "h3", quic_tp, quic_tp_len); - } - } - return 0; -} -#endif /* USE_WOLFSSL */ - -static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *user_data) -{ - struct curl_tls_ctx *ctx = user_data; - -#ifdef USE_OPENSSL -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) - != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#elif defined(OPENSSL_QUIC_API2) - /* nothing to do */ -#else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ - if(Curl_ssl_scache_use(cf, data)) { - /* Enable the session cache because it is a prerequisite for the - * "new session" callback. Use the "external storage" mode to prevent - * OpenSSL from creating an internal session cache. - */ - SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); - } - -#elif defined(USE_GNUTLS) - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { - failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); - return CURLE_FAILED_INIT; - } - if(Curl_ssl_scache_use(cf, data)) { - gnutls_handshake_set_hook_function(ctx->gtls.session, - GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, - quic_gtls_handshake_cb); - } - -#elif defined(USE_WOLFSSL) - if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } - if(Curl_ssl_scache_use(cf, data)) { - /* Register to get notified when a new session is received */ - wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb); - } -#endif - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct alpn_spec *alpns, - struct Curl_ssl_session *scs, - bool *do_early_data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_max = scs->earlydata_max; -#endif -#ifdef USE_GNUTLS - ctx->earlydata_max = - gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_max = scs->earlydata_max; -#else - ctx->earlydata_max = 0; -#endif /* WOLFSSL_EARLY_DATA */ -#endif -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if(!ctx->earlydata_max) { - CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); - } - else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { - CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); - } - else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); - } - else { - int rv; - rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( - ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); - if(rv) - CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " - "parameters: %s", ngtcp2_strerror(rv)); - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); - result = init_ngh3_conn(cf, data); - if(!result) { - ctx->use_earlydata = TRUE; - cf->connected = TRUE; - *do_early_data = TRUE; - } - } - } -#else /* not supported in the TLS backend */ - (void)data; - (void)ctx; - (void)scs; - (void)alpns; -#endif - return result; -} - -static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) -{ -#ifdef USE_OPENSSL - return Curl_ossl_need_httpsrr(data); -#elif defined(USE_WOLFSSL) - return Curl_wssl_need_httpsrr(data); -#else - (void)data; - return FALSE; -#endif -} - -/* - * Might be called twice for happy eyeballs. - */ -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc; - int rv; - CURLcode result; - const struct Curl_sockaddr_ex *sockaddr = NULL; - int qfd; - static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - - DEBUGASSERT(ctx->initialized); - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - ctx->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - ctx->qlogfd = qfd; /* -1 if failure above */ - quic_settings(ctx, data, pktx); - - result = vquic_ctx_init(data, &ctx->q); - if(result) - return result; - - if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) - return CURLE_QUIC_CONNECT_ERROR; - ctx->q.local_addrlen = sizeof(ctx->q.local_addr); - rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, - &ctx->q.local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, - cf_ngtcp2_tls_ctx_setup, &ctx->tls, - &ctx->conn_ref, - cf_ngtcp2_on_session_reuse); - if(result) - return result; - -#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) - if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); - return CURLE_FAILED_INIT; + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; } - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); - if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); - return CURLE_FAILED_INIT; + else { + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); } -#elif defined(USE_OPENSSL) - SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); -#elif defined(USE_GNUTLS) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); -#elif defined(USE_WOLFSSL) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); -#else -#error "ngtcp2 TLS backend not defined" -#endif - ngtcp2_ccerr_default(&ctx->last_error); + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= pktx->ts) { + CURLcode result; + int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + Curl_cf_ngtcp2_cmn_err_set(cf, data, rv); + return CURLE_SEND_ERROR; + } + result = Curl_cf_ngtcp2_progress_ingress(cf, data, pktx); + if(result) + return result; + result = Curl_cf_ngtcp2_progress_egress(cf, data, pktx); + if(result) + return result; + /* ask again, things might have changed */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + } + if(expiry > pktx->ts) { + ngtcp2_duration timeout = expiry - pktx->ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); + } + } return CURLE_OK; } -static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - struct pkt_io_ctx pktx; + const ngtcp2_transport_params *rp; + /* Peer should have sent us its transport parameters. If it + * announces a positive `max_idle_timeout` it closes the + * connection when it does not hear from us for that time. + * + * Some servers use this as a keep-alive timer at a rather low + * value. We are doing HTTP/3 here and waiting for the response + * to a request may take a considerable amount of time. We need + * to prevent the peer's QUIC stack from closing in this case. + */ + if(!ctx->qconn) + return; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(!rp || !rp->max_idle_timeout) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; + else if(!Curl_uint32_hash_count(&ctx->streams)) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); } - - *done = FALSE; - - if(cf_ngtcp2_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { - CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); - return CURLE_OK; + else { + ngtcp2_duration keep_ns; + keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); + CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " + "set keep-alive to %" PRIu64 " ms.", + (rp->max_idle_timeout / NGTCP2_MILLISECONDS), + (keep_ns / NGTCP2_MILLISECONDS)); } +} - pktx_init(&pktx, cf, data); - CF_DATA_SAVE(save, cf, data); +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!ctx->qconn) { - ctx->started_at = *Curl_pgrs_now(data); - result = cf_connect_start(cf, data, &pktx); - if(result) - goto out; - if(cf->connected) { - *done = TRUE; - goto out; - } - result = cf_progress_egress(cf, data, &pktx); - /* we do not expect to be able to recv anything yet */ - goto out; - } + if(!data) + return CURLE_FAILED_INIT; - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; + if(stream) + return CURLE_OK; - result = cf_progress_egress(cf, data, &pktx); - if(result) - goto out; + stream = curlx_calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; - if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { - result = ctx->tls_vrfy_result; - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - } - } + stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; -out: - if(ctx->qconn && - ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && - ngtcp2_conn_in_draining_period(ctx->qconn)) { - const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); + /* on send, we control how much we put into the buffer */ + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + stream->sendbuf_len_in_flight = 0; + stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - result = CURLE_COULDNT_CONNECT; - if(cerr) { - CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, - cerr->type, cerr->error_code); - switch(cerr->type) { - case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: - CURL_TRC_CF(data, cf, "error in version negotiation"); - break; - default: - if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { - CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", - (unsigned int)(cerr->error_code & 0xffU)); - } - else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { - CURL_TRC_CF(data, cf, "connection refused by server"); - /* When a QUIC server instance is shutting down, it may send us a - * CONNECTION_CLOSE with this code right away. We want - * to keep on trying in this case. */ - result = CURLE_WEIRD_SERVER_REPLY; - } - } - } + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { + Curl_cf_ngtcp2_h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; } -#ifdef CURLVERBOSE - if(result) { - struct ip_quadruple ip; + if(Curl_uint32_hash_count(&ctx->streams) == 1) + cf_ngtcp2_setup_keep_alive(cf, data); - if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) - infof(data, "QUIC connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); - } -#endif - if(!result && ctx->qconn) { - result = check_and_set_expiry(cf, data, &pktx); - } - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; + return CURLE_OK; } -static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; + DEBUGASSERT(data); + DEBUGASSERT(stream); + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - DEBUGASSERT(pres1); - CF_DATA_SAVE(save, cf, data); - /* Set after transport params arrived and continually updated - * by callback. QUIC counts the number over the lifetime of the - * connection, ever increasing. - * We count the *open* transfers plus the budget for new ones. */ - if(!ctx->qconn || ctx->shutdown_started) { - *pres1 = 0; - } - else if(ctx->max_bidi_streams) { - uint64_t avail_bidi_streams = 0; - uint64_t max_streams = cf->conn->attached_xfers; - if(ctx->max_bidi_streams > ctx->used_bidi_streams) - avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; - max_streams += avail_bidi_streams; - *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; - } - else /* transport params not arrived yet? take our default. */ - *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); - CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " - "MAX_CONCURRENT -> %d (%u in use)", - cf->conn->connection_id, *pres1, cf->conn->attached_xfers); - CF_DATA_RESTORE(cf, save); - return CURLE_OK; - } - case CF_QUERY_CONNECT_REPLY_MS: - if(ctx->q.got_first_byte) { - timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, - &ctx->started_at); - *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; - } - else - *pres1 = -1; - return CURLE_OK; - case CF_QUERY_TIMER_CONNECT: { - struct curltime *when = pres2; - if(ctx->q.got_first_byte) - *when = ctx->q.first_byte_at; - return CURLE_OK; - } - case CF_QUERY_TIMER_APPCONNECT: { - struct curltime *when = pres2; - if(cf->connected) - *when = ctx->handshake_at; - return CURLE_OK; - } - case CF_QUERY_HTTP_VERSION: - *pres1 = 30; - return CURLE_OK; - case CF_QUERY_SSL_INFO: - case CF_QUERY_SSL_CTX_INFO: { - struct curl_tlssessioninfo *info = pres2; - if(Curl_vquic_tls_get_ssl_info(&ctx->tls, - (query == CF_QUERY_SSL_CTX_INFO), info)) - return CURLE_OK; - break; - } - case CF_QUERY_ALPN_NEGOTIATED: { - const char **palpn = pres2; - DEBUGASSERT(palpn); - *palpn = cf->connected ? "h3" : NULL; - return CURLE_OK; + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", + stream->id, (int)result); } - default: - break; +} + +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + Curl_uint32_hash_remove(&ctx->streams, data->mid); + if(!Curl_uint32_hash_count(&ctx->streams)) + cf_ngtcp2_setup_keep_alive(cf, data); } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; } -static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) { struct cf_ngtcp2_ctx *ctx = cf->ctx; bool alive = FALSE; @@ -2928,8 +1917,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, not in use by any other transfer, there should not be any data here, only "protocol frames" */ *input_pending = FALSE; - result = cf_progress_ingress(cf, data, NULL); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", (int)result); alive = result ? FALSE : TRUE; } @@ -2938,60 +1927,43 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, return alive; } -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, - 0, - cf_ngtcp2_destroy, - cf_ngtcp2_connect, - cf_ngtcp2_close, - cf_ngtcp2_shutdown, - cf_ngtcp2_adjust_pollset, - Curl_cf_def_data_pending, - cf_ngtcp2_send, - cf_ngtcp2_recv, - cf_ngtcp2_cntrl, - cf_ngtcp2_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_ngtcp2_query, -}; - -CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr) +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + int rc; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } - cf_ngtcp2_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - cf->conn = conn; - - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); - if(result) - goto out; - cf->next->conn = cf->conn; - cf->next->sockindex = cf->sockindex; - -out: - *pcf = (!result) ? cf : NULL; - if(result) { - if(cf) - Curl_conn_cf_discard_chain(&cf, data); - else if(ctx) - cf_ngtcp2_ctx_free(ctx); + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } - return result; + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack encoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack decoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ diff --git a/deps/curl/lib/vquic/cf-ngtcp2-cmn.h b/deps/curl/lib/vquic/cf-ngtcp2-cmn.h new file mode 100644 index 0000000000..88554edfb6 --- /dev/null +++ b/deps/curl/lib/vquic/cf-ngtcp2-cmn.h @@ -0,0 +1,239 @@ +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#include +#include + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) +#include +#elif defined(OPENSSL_QUIC_API2) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#include +#include +#include +#include "vtls/wolfssl.h" +#endif + +#ifdef HAVE_NETINET_UDP_H +#include +#endif + +#include + +#include "http1.h" +#include "uint-hash.h" +#include "vtls/vtls.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" + +struct Curl_cfilter; +struct Curl_easy; +struct cf_ngtcp2_ctx; +struct cf_quic_ctx; + +#define QUIC_MAX_STREAMS (256 * 1024) +#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) +#define QUIC_TUNNEL_INBUF_SIZE (64 * 1024) + +/* We announce a small window size in transport param to the server, + * and grow that immediately to max when no rate limit is in place. + * We need to start small as we are not able to decrease it. */ +#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024) +#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) +#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX) + +#define H3_STREAM_CHUNK_SIZE (64 * 1024) +#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE +#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE +#endif +/* The pool keeps spares around and half of a full stream window + * seems good. More does not seem to improve performance. + * The benefit of the pool is that stream buffers do not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#define H3_STREAM_POOL_SPARES 2 +/* The max amount of un-acked upload data we keep around per stream */ +#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) +#define H3_STREAM_SEND_CHUNKS \ + (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 + + +void Curl_ngtcp2_ver(char *p, size_t len); + +typedef CURLcode cf_ngtcp2_init_h3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); + +struct cf_ngtcp2_ctx { + struct cf_quic_ctx q; + struct ssl_peer ssl_peer; + struct curl_tls_ctx tls; +#ifdef OPENSSL_QUIC_API2 + ngtcp2_crypto_ossl_ctx *ossl_ctx; +#endif + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_ccerr last_error; + ngtcp2_crypto_conn_ref conn_ref; + struct cf_call_data call_data; + cf_ngtcp2_init_h3_conn *init_h3_conn_cb; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; /* hash data->mid to h3_stream_ctx */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ + int qlogfd; + unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ + size_t tunnel_inbuf_len; + BIT(initialized); + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ +}; + +/* How to access `call_data` from a cf_ngtcp2 filter */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data + +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb); +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx); +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +/** + * All about the H3 internals of a stream + */ +struct h3_stream_ctx { + int64_t id; /* HTTP/3 stream identifier */ + struct bufq sendbuf; /* h3 request body */ + struct h1_req_parser h1; /* h1 request parsing */ + size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ + uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t rx_total; /* total number of bytes received */ + uint64_t rx_offset; /* current receive offset */ + uint64_t rx_offset_max; /* allowed receive offset */ + uint64_t window_size_max; /* max flow control window set for stream */ + int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is local closed */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ +}; + +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data); + +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +struct cf_ngtcp2_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + ngtcp2_path_storage ps; +}; + +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx, + struct Curl_cfilter *cf); + +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data); + +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending); + +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ + +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H */ diff --git a/deps/curl/lib/vquic/cf-ngtcp2-proxy.c b/deps/curl/lib/vquic/cf-ngtcp2-proxy.c new file mode 100644 index 0000000000..fd2840890f --- /dev/null +++ b/deps/curl/lib/vquic/cf-ngtcp2-proxy.c @@ -0,0 +1,1316 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +#include "urldata.h" +#include "url.h" +#include "curl_trc.h" +#include "sendf.h" +#include "multiif.h" +#include "cfilters.h" +#include "connect.h" +#include "progress.h" +#include "curlx/dynbuf.h" +#include "http_proxy.h" +#include "vquic/vquic.h" +#include "vquic/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2-proxy.h" +#include "capsule.h" + +/* A stream window is the maximum amount we need to buffer for + * each active transfer. We use HTTP/3 flow control and only ACK + * when we take things out of the buffer. + * Chunk size is large enough to take a full DATA frame */ +#define PROXY_H3_STREAM_RECV_CHUNKS ((512 * 1024) / H3_STREAM_CHUNK_SIZE) + +typedef enum { + H3_TUNNEL_INIT, /* init/default/no tunnel state */ + H3_TUNNEL_CONNECT, /* CONNECT request is being sent */ + H3_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H3_TUNNEL_ESTABLISHED, + H3_TUNNEL_FAILED +} h3_tunnel_state; + +struct h3_tunnel_stream { + struct Curl_peer *peer; /* where the tunnel goes to */ + struct http_resp *resp; + struct bufq recvbuf; + char *authority; + struct h3_stream_ctx *stream; + h3_tunnel_state state; + BIT(udp); + BIT(has_final_response); + BIT(closed); +}; + +static CURLcode h3_tunnel_stream_init(struct h3_tunnel_stream *ts, + struct Curl_peer *peer, + bool udp) +{ + ts->state = H3_TUNNEL_INIT; + Curl_peer_link(&ts->peer, peer); + Curl_bufq_init2(&ts->recvbuf, H3_STREAM_CHUNK_SIZE, + PROXY_H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + ts->udp = udp; + /* host:port with IPv6 support */ + ts->authority = curl_maprintf("%s%s%s:%u", peer->ipv6 ? "[" : "", + peer->hostname, + peer->ipv6 ? "]" : "", + peer->port); + if(!ts->authority) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void h3_tunnel_stream_reset(struct h3_tunnel_stream *ts) +{ + Curl_bufq_reset(&ts->recvbuf); + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + ts->stream = NULL; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_stream_cleanup(struct h3_tunnel_stream *ts) +{ + Curl_peer_unlink(&ts->peer); + Curl_bufq_free(&ts->recvbuf); + Curl_http_resp_free(ts->resp); + curlx_safefree(ts->authority); + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_go_state(struct Curl_cfilter *cf, + struct h3_tunnel_stream *ts, + h3_tunnel_state new_state, + struct Curl_easy *data) +{ + VERBOSE(int64_t stream_id = ts->stream ? ts->stream->id : -1); + (void)cf; + + if(ts->state == new_state) + return; + + /* leaving this one */ + switch(ts->state) { + case H3_TUNNEL_CONNECT: + data->req.ignorebody = FALSE; + break; + default: + break; + } + + /* entering this one */ + switch(new_state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [init]", stream_id); + h3_tunnel_stream_reset(ts); + break; + case H3_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [connect]", stream_id); + ts->state = H3_TUNNEL_CONNECT; + break; + case H3_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [response]", stream_id); + ts->state = H3_TUNNEL_RESPONSE; + break; + case H3_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [established]", stream_id); + infof(data, "CONNECT%s phase completed for HTTP/3 proxy", + ts->udp ? "-UDP" : ""); + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + ts->state = new_state; + curlx_safefree(data->req.hd_proxy_auth); + break; + case H3_TUNNEL_FAILED: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [failed]", stream_id); + ts->state = new_state; + /* If a proxy-authorization header was used for the proxy, then we should + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ + curlx_safefree(data->req.hd_proxy_auth); + break; + } +} + +struct cf_h3_proxy_ctx { + struct cf_ngtcp2_ctx ngtcp2_ctx; + struct h3_tunnel_stream tunnel; /* our tunnel CONNECT stream */ + BIT(connected); +}; + +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); + +static CURLcode cf_h3_proxy_ctx_init(struct cf_h3_proxy_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + CURLcode result; + result = Curl_cf_ngtcp2_ctx_init(&ctx->ngtcp2_ctx, origin, peer, + sslc, cf_ngtcp2_proxy_h3_init); + if(!result) + result = h3_tunnel_stream_init(&ctx->tunnel, tunnel_peer, + TRNSPRT_IS_DGRAM(tunnel_transport)); + return result; +} + +static void cf_h3_proxy_ctx_free(struct cf_h3_proxy_ctx *ctx) +{ + if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(&ctx->ngtcp2_ctx); + h3_tunnel_stream_cleanup(&ctx->tunnel); + curlx_free(ctx); + } +} + +static int cb_h3_proxy_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct h3_stream_ctx *stream; + size_t skiplen; + (void)stream_user_data; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) + return 0; + + /* The server acknowledged `datalen` of bytes from our request body. + * This is a delta. We have kept this data in `sendbuf` for + * re-transmissions and can free it now. */ + if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) + skiplen = stream->sendbuf_len_in_flight; + else + skiplen = (size_t)datalen; + Curl_bufq_skip(&stream->sendbuf, skiplen); + stream->sendbuf_len_in_flight -= skiplen; + + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int cb_h3_proxy_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) + return 0; + + stream->closed = TRUE; + stream->error3 = app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, + stream->id, stream->error3); + } + else + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; + Curl_multi_mark_dirty(data); + return 0; +} + +static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; + + /* how much does rate limiting allow us to acknowledge? */ + if(Curl_rlimit_active(&data->progress.dl.rlimit)) { + int64_t avail; + + /* start rate limit updates only after first bytes arrived */ + if(!stream->rx_offset) + return; + + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); + if(avail <= 0) { + /* nothing available, do not extend the rx offset */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 + " tokens)", stream->id, avail); + return; + } + wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); + } + + if(stream->rx_offset_max < stream->rx_offset) { + DEBUGASSERT(0); + return; + } + cur_win = stream->rx_offset_max - stream->rx_offset; + if(cur_win < wanted_win) { + /* We have exhausted the credit we gave the QUIC peer for DATA. + * We extend it with the amount we can give (rate limit) */ + uint64_t ext = wanted_win - cur_win; + + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, ext); + ngtcp2_conn_extend_max_offset(ctx->qconn, ext); + stream->rx_offset_max += ext; + if(stream->rx_offset_max > stream->window_size_max) { + stream->window_size_max = stream->rx_offset_max; + CURL_TRC_CF(data, cf, "[%" PRId64 "] max window now -> %" PRIu64, + stream->id, stream->window_size_max); + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx_offset_max -> %" PRIu64 + " (ext %" PRIu64 ", win %" PRIu64 ")", + stream->id, stream->rx_offset_max, ext, wanted_win); + } +} + +static int cb_h3_proxy_recv_data(nghttp3_conn *conn, int64_t stream3_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + size_t nwritten; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream3_id; + (void)stream_user_data; + + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream3_id)) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + stream->rx_total += (curl_off_t)buflen; + CURL_TRC_CF(data, cf, "[cb_h3_proxy_recv_data] " + "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, + stream->id, buflen, stream->rx_total); + + result = Curl_bufq_write(&pctx->tunnel.recvbuf, buf, buflen, &nwritten); + if(result || (nwritten < buflen)) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + /* DATA has been moved into our local recv buffer. Update stream offsets + * and give QUIC read credit back so long transfers over proxy tunnels + * do not stall on stream/connection flow-control limits. */ + stream->rx_offset += buflen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; + + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); + cf_h3_proxy_upd_rx_win(cf, data, stream); + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + (void)conn; + (void)stream_user_data; + + if(!ctx) + return 0; + + /* nghttp3 has consumed bytes on the QUIC stream and we need to + * tell the QUIC connection to increase its flow control */ + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + + return 0; +} + +static int cb_h3_proxy_recv_header(nghttp3_conn *conn, int64_t 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; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + CURLcode result = CURLE_OK; + int http_status; + struct http_resp *resp; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)stream_user_data; + + /* stream_user_data might be NULL for control streams */ + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] recv_header: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + return 0; + } + + if(pctx->tunnel.has_final_response) { + /* we do not do anything with trailers for tunnel streams */ + return 0; + } + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + http_status = stream->status_code; + result = Curl_http_resp_make(&resp, http_status, NULL); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + if(pctx->tunnel.resp) + Curl_http_resp_free(pctx->tunnel.resp); + pctx->tunnel.resp = resp; + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, + (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); + result = Curl_dynhds_add(&pctx->tunnel.resp->headers, + (const char *)h3name.base, h3name.len, + (const char *)h3val.base, h3val.len); + if(result) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int cb_h3_proxy_end_headers(nghttp3_conn *conn, int64_t stream_id, + int fin, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + (void)conn; + (void)stream_id; + (void)fin; + (void)stream_user_data; + + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + return 0; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, + stream->status_code); + + if(!pctx->tunnel.has_final_response) { + if(stream->status_code / 100 != 1) { + pctx->tunnel.has_final_response = TRUE; + } + } + + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + (void)conn; + (void)stream_user_data; + + if(ctx) { + int rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); + + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + int rv; + + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + if(!pctx->tunnel.stream || + (stream_id != pctx->tunnel.stream->id)) + return 0; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static nghttp3_ssize cb_h3_tunnel_read_data(nghttp3_conn *conn, + int64_t stream_id, + nghttp3_vec *vec, + size_t veccnt, + uint32_t *pflags, + void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + size_t nwritten = 0; + size_t nvecs = 0; + const unsigned char *buf_base; + + (void)conn; + (void)stream_id; + (void)veccnt; + (void)stream_user_data; + (void)pflags; + + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream_id)) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_proxy_acked_req_body()` for updates). + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * that we have already passed to nghttp3, but which have not been + * ACKed yet. + * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * to nghttp3. Do that now, if we can. */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + nvecs = 0; + while(nvecs < veccnt) { + if(!Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + &buf_base, + &vec[nvecs].len)) + break; + vec[nvecs].base = (uint8_t *)(uintptr_t)buf_base; + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been able to peek */ + } + + if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + stream->id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%zu vecs%s with %zu (buffered=%zu)", + stream->id, nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", + nwritten, Curl_bufq_len(&stream->sendbuf)); + return (nghttp3_ssize)nvecs; +} + +static nghttp3_callbacks ngh3_proxy_callbacks = { + cb_h3_proxy_acked_req_body, /* acked_stream_data */ + cb_h3_proxy_stream_close, + cb_h3_proxy_recv_data, + cb_h3_proxy_deferred_consume, + NULL, /* begin_headers */ + cb_h3_proxy_recv_header, + cb_h3_proxy_end_headers, + NULL, /* begin_trailers */ + cb_h3_proxy_recv_header, + NULL, /* end_trailers */ + cb_h3_proxy_stop_sending, + NULL, /* end_stream */ + cb_h3_proxy_reset_stream, + NULL, /* shutdown */ + NULL, /* recv_settings (deprecated) */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ + NULL, /* recv_origin */ + NULL, /* end_origin */ + NULL, /* rand */ +#endif +#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ + NULL, /* recv_settings2 */ +#endif +}; + +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) +{ + int rc; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_proxy_callbacks, + &ctx->h3settings, + Curl_nghttp3_mem(), + cf); + if(rc) { + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; + } + + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); +} + +static ssize_t cf_h3_proxy_recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + CURLcode *err) +{ + ssize_t nread = -1; + *err = CURLE_OK; + + if(stream->reset) { + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); + data->state.refused_stream = TRUE; + *err = CURLE_RECV_ERROR; + goto out; + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + nread = 0; + goto out; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; + goto out; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + *err = CURLE_HTTP3; + goto out; + } + nread = 0; + +out: + return nread; +} + +static CURLcode cf_h3_proxy_sendbuf_add(struct Curl_easy *data, + struct h3_stream_ctx *stream, + const uint8_t *buf, size_t len, + size_t *pnwritten) +{ + CURLcode result; + *pnwritten = 0; + (void)data; + + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + return result; +} + +static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + *pnwritten = 0; + + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + (void)eos; /* use for stream EOF and block handling */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + if(pctx->tunnel.closed) { + result = CURLE_SEND_ERROR; + goto denied; + } + + stream = pctx->tunnel.stream; + if(!stream) { + result = CURLE_SEND_ERROR; + goto denied; + } + + if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; + } + else { + result = cf_h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, (int)result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + + DEBUGASSERT(!result); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; +} + +/* incoming data frames on the h3 stream */ +static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = pctx->tunnel.stream; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *pnread = 0; + + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + result = CURLE_RECV_ERROR; + goto out; + } + + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); + if(result) + goto out; + } + + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + /* inbufq had nothing before, maybe after progressing ingress? */ + if(!*pnread && !Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); + if(result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) -> %zu, %d", + stream->id, len, *pnread, (int)result); + goto out; + } + } + + if(*pnread) { + Curl_multi_mark_dirty(data); + } + else { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + ssize_t nread = + cf_h3_proxy_recv_closed_stream(cf, data, stream, &result); + if(nread > 0) + *pnread = (size_t)nread; + goto out; + } + result = CURLE_AGAIN; + } + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnread); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h3_proxy_submit(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + struct httpreq *req) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; + struct dynhds h2_headers; + nghttp3_nv *nva = NULL; + size_t nheader; + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + CURLcode result; + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + result = Curl_http_req_to_h2(&h2_headers, req, data); + if(result) + goto out; + + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); + if(result) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + result = CURLE_FAILED_INIT; + goto out; + } + + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + /* Open a bidirectional stream */ + { + int64_t sid; + int rv; + + DEBUGASSERT(stream->id == -1); + /* Do NOT set `data` as stream user data. The transfer `data` may + * get cleaned up long before the tunnel goes down. */ + rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, NULL); + if(rv) { + failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); + result = CURLE_SEND_ERROR; + goto out; + } + stream->id = sid; + ts->stream = stream; + ++ctx->used_bidi_streams; + CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); + } + + /* CONNECT-UDP request stream remains open for capsules, no fixed EOF. */ + stream->send_closed = 0; + reader.read_data = cb_h3_tunnel_read_data; + preader = &reader; + + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->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->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", + stream->id, rc, nghttp3_strerror(rc)); + break; + } + result = CURLE_SEND_ERROR; + goto out; + } + + if(Curl_trc_is_verbose(data)) { + CURL_TRC_CF(data, cf, "[H3-PROXY] [%" PRId64 "] OPENED stream " + "for %s", stream->id, + Curl_bufref_ptr(&data->state.url)); + } + +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + return result; +} + +static CURLcode cf_h3_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); + + if(!ctx->qconn || !pctx->tunnel.stream || (sock == CURL_SOCKET_BAD)) + return CURLE_OK; + + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + + if(want_recv || want_send || !Curl_bufq_is_empty(&ctx->q.sendbuf)) { + struct h3_stream_ctx *stream = pctx->tunnel.stream; + bool c_exhaust, s_exhaust; + + c_exhaust = want_send && + (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); + s_exhaust = want_send && stream && stream->id >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + } + return result; +} + +static bool cf_h3_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) + return TRUE; + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static CURLcode cf_h3_proxy_submit_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, + ts->peer, + PROXY_HTTP_V3, + (bool)ts->udp); + if(!result) + result = Curl_creader_set_null(data); + if(!result) + result = cf_h3_proxy_submit(cf, data, ts, req); + + if(req) + Curl_http_req_free(req); + if(result) + failf(data, "Failed sending CONNECT to proxy"); + return result; +} + +static CURLcode cf_h3_proxy_inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; + + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)pctx->tunnel.udp, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_ESTABLISHED, data); + break; + case PROXY_INSPECT_FAILED: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_INIT, data); + break; + } + return result; +} + +static CURLcode cf_h3_proxy_tunnel(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + bool *pdone) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(ts); + DEBUGASSERT(ts->authority); + *pdone = FALSE; + do { + switch(ts->state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); + result = cf_h3_proxy_submit_CONNECT(cf, data, ts); + if(result) + goto out; + h3_tunnel_go_state(cf, ts, H3_TUNNEL_CONNECT, data); + + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result) + goto out; + FALLTHROUGH(); + + case H3_TUNNEL_CONNECT: + /* Non-blocking: call ingress/egress once and return. + * The multi interface will call us again when ready. */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); + if(result) + goto out; + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result && result != CURLE_AGAIN) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + goto out; + } + + if(ts->has_final_response) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_RESPONSE, data); + } + else { + /* Not done yet, return and let multi interface call us again */ + result = CURLE_OK; + goto out; + } + FALLTHROUGH(); + + case H3_TUNNEL_RESPONSE: + DEBUGASSERT(ts->has_final_response); + result = cf_h3_proxy_inspect_response(cf, data, ts); + if(result) + goto out; + ctx->connected = TRUE; + break; + + case H3_TUNNEL_ESTABLISHED: + *pdone = TRUE; + return CURLE_OK; + + case H3_TUNNEL_FAILED: + return CURLE_RECV_ERROR; + + default: + break; + } + + } while(ts->state == H3_TUNNEL_INIT); + +out: + if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + return result; +} + +static CURLcode cf_h3_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_call_data save = { 0 }; + CURLcode result = CURLE_OK; + struct h3_tunnel_stream *ts = &pctx->tunnel; + bool data_saved = FALSE; + + result = Curl_cf_ngtcp2_cmn_connect(cf, data, done); + if(result || !*done) + goto out; + + CF_DATA_SAVE(save, cf, data); + data_saved = TRUE; + + /* At this point the QUIC is connected, but the proxy is not connected */ + result = cf_h3_proxy_tunnel(cf, data, ts, done); + +out: + if(*done) { + cf->connected = TRUE; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + } + + if(data_saved) + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + CURL_TRC_CF(data, cf, "cf_h3_proxy_destroy()"); + cf_h3_proxy_ctx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + return Curl_cf_ngtcp2_cmn_shutdown(cf, data, done); +} + +struct Curl_cftype Curl_cft_h3_proxy = { + "H3-PROXY", + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SSL, + CURL_LOG_LVL_NONE, + cf_h3_proxy_destroy, + cf_h3_proxy_connect, + cf_h3_proxy_shutdown, + cf_h3_proxy_adjust_pollset, + cf_h3_proxy_data_pending, + cf_h3_proxy_send, + cf_h3_proxy_recv, + Curl_cf_def_cntrl, + Curl_cf_ngtcp2_cmn_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if(!tunnel_peer) + return CURLE_FAILED_INIT; + if((transport_peer != TRNSPRT_QUIC) || (!conn->http_proxy.peer)) + return CURLE_FAILED_INIT; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = cf_h3_proxy_ctx_init(ctx, origin, peer, &conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + cf->conn = conn; + + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); + if(result) + goto out; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; + +out: + *pcf = (!result) ? cf : NULL; + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + else + CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->tunnel.udp); + return result; +} + +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + (void)data; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + result = cf_h3_proxy_ctx_init(ctx, origin, peer, + &cf_at->conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + + /* H3-PROXY uses the UDP socket created by happy eyeballs below it. + Curl_conn_cf_insert_after chains the existing sub-filters, i.e. + "HAPPY-EYEBALLS -> UDP" as cf->next of H3-PROXY. */ + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + return result; +} + +#endif diff --git a/deps/curl/lib/vquic/cf-ngtcp2-proxy.h b/deps/curl/lib/vquic/cf-ngtcp2-proxy.h new file mode 100644 index 0000000000..acdee0e463 --- /dev/null +++ b/deps/curl/lib/vquic/cf-ngtcp2-proxy.h @@ -0,0 +1,52 @@ +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +#endif + +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H */ diff --git a/deps/curl/lib/vquic/cf-ngtcp2.c b/deps/curl/lib/vquic/cf-ngtcp2.c new file mode 100644 index 0000000000..be1a3257c3 --- /dev/null +++ b/deps/curl/lib/vquic/cf-ngtcp2.c @@ -0,0 +1,1166 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#include "urldata.h" +#include "url.h" +#include "uint-hash.h" +#include "curl_trc.h" +#include "rand.h" +#include "multiif.h" +#include "cfilters.h" +#include "cf-dns.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "curlx/fopen.h" +#include "curlx/dynbuf.h" +#include "http1.h" +#include "select.h" +#include "transfer.h" +#include "bufref.h" +#include "vquic/vquic.h" +#include "vquic/vquic_int.h" +#include "vquic/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2.h" + + +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data); + +static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); + + if(!ctx->qconn || (sock == CURL_SOCKET_BAD)) + return CURLE_OK; + + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) + want_send = TRUE; + + if(want_recv || want_send) { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; + + CF_DATA_SAVE(save, cf, data); + c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || + !ngtcp2_conn_get_max_data_left(ctx->qconn)); + s_exhaust = want_send && stream && stream->id >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + return result; +} + +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 cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(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 %" PRIu64, + stream->id, stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + } + Curl_multi_mark_dirty(data); + return 0; +} + +static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t buflen, bool eos) +{ + /* This function returns no error intentionally, but records + * the result at the stream, skipping further writes once the + * `result` of the transfer is known. + * The stream is subsequently cancelled "higher up" in the filter's + * send/recv callbacks. Closing the stream here leads to SEND/RECV + * errors in other places that then overwrite the transfer's result. */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, buflen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " + "bytes of headers", stream->id, (int)stream->xfer_result, + buflen); + } +} + +static void h3_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t buflen, bool eos) +{ + /* This function returns no error intentionally, but records + * the result at the stream, skipping further writes once the + * `result` of the transfer is known. + * The stream is subsequently cancelled "higher up" in the filter's + * send/recv callbacks. Closing the stream here leads to SEND/RECV + * errors in other places that then overwrite the transfer's result. */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp(data, buf, buflen, eos); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", + stream->id, (int)stream->xfer_result, buflen); + } + } +} + +static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; + + /* how much does rate limiting allow us to acknowledge? */ + if(Curl_rlimit_active(&data->progress.dl.rlimit)) { + int64_t avail; + + /* start rate limit updates only after first bytes arrived */ + if(!stream->rx_offset) + return; + + avail = Curl_rlimit_avail(&data->progress.dl.rlimit, Curl_pgrs_now(data)); + if(avail <= 0) { + /* nothing available, do not extend the rx offset */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] dl rate limit exhausted (%" PRId64 + " tokens)", stream->id, avail); + return; + } + wanted_win = CURLMIN((uint64_t)avail, H3_STREAM_WINDOW_SIZE_MAX); + } + + if(stream->rx_offset_max < stream->rx_offset) { + DEBUGASSERT(0); + return; + } + cur_win = stream->rx_offset_max - stream->rx_offset; + + if(wanted_win > cur_win) { + uint64_t delta = wanted_win - cur_win; + + if(UINT64_MAX - delta < stream->rx_offset_max) + delta = UINT64_MAX - stream->rx_offset_max; + if(delta) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx window, extend by %" PRIu64 + " bytes", stream->id, delta); + stream->rx_offset_max += delta; + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, delta); + } + } +} + +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 cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + + (void)conn; + (void)stream3_id; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + h3_xfer_write_resp(cf, data, stream, (const char *)buf, buflen, FALSE); + + ngtcp2_conn_extend_max_offset(ctx->qconn, buflen); + stream->rx_offset += buflen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; + + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); + cf_ngtcp2_upd_rx_win(cf, data, stream); + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + + /* nghttp3 has consumed bytes on the QUIC stream and we need to + * tell the QUIC connection to increase its flow control */ + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + if(stream) { + stream->rx_offset += consumed; + stream->rx_offset_max += consumed; + } + 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 cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + (void)fin; + (void)cf; + + if(!stream) + return 0; + /* add a CRLF only if we have received some headers */ + h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), + (bool)stream->closed); + + 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; + } + Curl_multi_mark_dirty(data); + 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; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(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) { + + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + curlx_dyn_reset(&ctx->scratch); + result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), + curlx_dyn_len(&ctx->scratch), FALSE); + CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", + stream_id, curlx_dyn_ptr(&ctx->scratch)); + if(result) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + 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); + curlx_dyn_reset(&ctx->scratch); + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3name.base, h3name.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), + curlx_dyn_len(&ctx->scratch), FALSE); + } + 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 cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + 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 cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + int rv; + (void)conn; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_req_body, /* 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 (deprecated) */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ + NULL, /* recv_origin */ + NULL, /* end_origin */ + NULL, /* rand */ +#endif +#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ + NULL, /* recv_settings2 */ +#endif +}; + +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) +{ + int rc; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_callbacks, + &ctx->h3settings, + Curl_nghttp3_mem(), + cf); + if(rc) { + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; + } + + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); +} + +static CURLcode recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + size_t *pnread) +{ + (void)cf; + *pnread = 0; + if(stream->reset) { + if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { + infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " + "on a new connection", stream->id); + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ + data->state.refused_stream = TRUE; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ + } + else if(stream->resp_hds_complete && data->req.no_body) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " + "but we did not want a body anyway, ignore error 0x%" + PRIx64 " %s", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return CURLE_OK; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + return CURLE_HTTP3; + } + return CURLE_OK; +} + +/* incoming data frames on the h3 stream */ +static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t buflen, size_t *pnread) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + int i; + + (void)ctx; + (void)buf; + NOVERBOSE((void)buflen); + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *pnread = 0; + + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + result = CURLE_RECV_ERROR; + goto out; + } + + cf_ngtcp2_upd_rx_win(cf, data, stream); + + /* first check for results/closed already known without touching + * the connection. For an already failed/closed stream, errors on + * the connection do not count. + * Then handle incoming data and check for failed/closed again. + */ + for(i = 0; i < 2; ++i) { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + result = recv_closed_stream(cf, data, stream, pnread); + goto out; + } + + if(!i && Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx)) { + result = CURLE_RECV_ERROR; + goto out; + } + } + + result = CURLE_AGAIN; + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(buflen=%zu) -> %d, %zu", + stream ? stream->id : -1, buflen, (int)result, *pnread); + CF_DATA_RESTORE(cf, save); + return result; +} + +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(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; + + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static 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 cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + size_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, + CURL_UNCONST(&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->id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%zu vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + stream->id, nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); + return (nghttp3_ssize)nvecs; +} + +static CURLcode h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + size_t *pnwritten) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = NULL; + int64_t sid; + struct dynhds h2_headers; + size_t nheader; + nghttp3_nv *nva = NULL; + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + CURLcode result; + + *pnwritten = 0; + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); + if(result) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + result = CURLE_FAILED_INIT; + goto out; + } + + result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, + !data->state.http_ignorecustom ? + data->set.str[STRING_CUSTOMREQUEST] : NULL, + 0, pnwritten); + if(result) + goto out; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(result) + goto out; + + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + if(rc) { + failf(data, "cannot open bidi streams"); + result = CURLE_SEND_ERROR; + goto out; + } + stream->id = sid; + ++ctx->used_bidi_streams; + + 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 no 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->h3conn, stream->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->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); + break; + } + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = CURLE_SEND_ERROR; + goto out; + } + + cf_ngtcp2_upd_rx_win(cf, data, stream); + + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, Curl_bufref_ptr(&data->state.url)); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } + +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + return result; +} + +static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const uint8_t *buf, size_t len, bool eos, + size_t *pnwritten) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + *pnwritten = 0; + + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + (void)eos; /* use for stream EOF and block handling */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + if(!stream || stream->id < 0) { + if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); + result = CURLE_SEND_ERROR; + goto out; + } + result = h3_stream_open(cf, data, buf, len, pnwritten); + if(result) { + CURL_TRC_CF(data, cf, "failed to open stream -> %d", (int)result); + goto out; + } + VERBOSE(stream = H3_STREAM_CTX(ctx, data)); + } + else if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; + } + else if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot send on closed connection"); + result = CURLE_SEND_ERROR; + goto out; + } + else { + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, (int)result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + + DEBUGASSERT(!result); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams + * windows. As we do in HTTP/2. */ + (void)cf; + if(!pause) + Curl_multi_mark_dirty(data); + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + 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_DONE: + Curl_cf_ngtcp2_h3_stream_done(cf, data); + break; + case CF_CTRL_DATA_DONE_SEND: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + if(stream && !stream->send_closed) { + stream->send_closed = TRUE; + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + break; + } + case CF_CTRL_CONN_INFO_UPDATE: + if(!cf->sockindex && cf->connected) { + cf->conn->httpversion_seen = 30; + Curl_conn_set_multiplex(cf->conn); + } + break; + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(!ctx->initialized) + return; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + } + ctx->qlogfd = -1; + Curl_vquic_tls_cleanup(&ctx->tls); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); + vquic_ctx_free(&ctx->q); + if(ctx->h3conn) { + nghttp3_conn_del(ctx->h3conn); + ctx->h3conn = NULL; + } + if(ctx->qconn) { + ngtcp2_conn_del(ctx->qconn); + ctx->qconn = NULL; + } +#ifdef OPENSSL_QUIC_API2 + if(ctx->ossl_ctx) { + ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); + ctx->ossl_ctx = NULL; + } +#endif + ctx->call_data = save; +} + +static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + if(ctx) { + if(ctx->qconn) { + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); + Curl_cf_ngtcp2_cmn_conn_close(cf, data); + cf_ngtcp2_ctx_close(ctx); + CF_DATA_RESTORE(cf, save); + } + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return Curl_cf_ngtcp2_cmn_connect(cf, data, done); +} + +static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + DEBUGASSERT(pres1); + CF_DATA_SAVE(save, cf, data); + /* Set after transport params arrived and continually updated + * by callback. QUIC counts the number over the lifetime of the + * connection, ever increasing. + * We count the *open* transfers plus the budget for new ones. */ + if(!ctx->qconn || ctx->shutdown_started) { + *pres1 = 0; + } + else if(ctx->max_bidi_streams) { + uint64_t avail_bidi_streams = 0; + uint64_t max_streams = cf->conn->attached_xfers; + if(ctx->max_bidi_streams > ctx->used_bidi_streams) + avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; + max_streams += avail_bidi_streams; + *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; + } + else /* transport params not arrived yet? take our default. */ + *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " + "MAX_CONCURRENT -> %d (%u in use)", + cf->conn->connection_id, *pres1, cf->conn->attached_xfers); + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->q.got_first_byte) { + timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, + &ctx->started_at); + *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + case CF_QUERY_TIMER_CONNECT: { + struct curltime *when = pres2; + if(ctx->q.got_first_byte) + *when = ctx->q.first_byte_at; + return CURLE_OK; + } + case CF_QUERY_TIMER_APPCONNECT: { + struct curltime *when = pres2; + if(cf->connected) + *when = ctx->handshake_at; + return CURLE_OK; + } + case CF_QUERY_HTTP_VERSION: + *pres1 = 30; + return CURLE_OK; + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + if(Curl_vquic_tls_get_ssl_info(&ctx->tls, + (query == CF_QUERY_SSL_CTX_INFO), info)) + return CURLE_OK; + break; + } + case CF_QUERY_ALPN_NEGOTIATED: { + const char **palpn = pres2; + DEBUGASSERT(palpn); + *palpn = cf->connected ? "h3" : NULL; + return CURLE_OK; + } + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, + 0, + cf_ngtcp2_destroy, + cf_ngtcp2_connect, + Curl_cf_ngtcp2_cmn_shutdown, + cf_ngtcp2_adjust_pollset, + Curl_cf_def_data_pending, + cf_ngtcp2_send, + cf_ngtcp2_recv, + cf_ngtcp2_cntrl, + Curl_cf_ngtcp2_cmn_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_ngtcp2_query, +}; + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + cf->conn = conn; + + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); + if(result) + goto out; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; + +out: + *pcf = (!result) ? cf : NULL; + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } + } + return result; +} + +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &cf_at->conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + if(result) { + curlx_safefree(cf); + if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } + } + return result; +} + +#endif diff --git a/deps/curl/lib/vquic/curl_ngtcp2.h b/deps/curl/lib/vquic/cf-ngtcp2.h similarity index 81% rename from deps/curl/lib/vquic/curl_ngtcp2.h rename to deps/curl/lib/vquic/cf-ngtcp2.h index 185272ace0..601efc8224 100644 --- a/deps/curl/lib/vquic/curl_ngtcp2.h +++ b/deps/curl/lib/vquic/cf-ngtcp2.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H -#define HEADER_CURL_VQUIC_CURL_NGTCP2_H +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -48,12 +48,16 @@ struct Curl_cfilter; #include "urldata.h" -void Curl_ngtcp2_ver(char *p, size_t len); - CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); + +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_H */ diff --git a/deps/curl/lib/vquic/curl_quiche.c b/deps/curl/lib/vquic/cf-quiche.c similarity index 95% rename from deps/curl/lib/vquic/curl_quiche.c rename to deps/curl/lib/vquic/cf-quiche.c index 73f664a653..31a3957ec3 100644 --- a/deps/curl/lib/vquic/curl_quiche.c +++ b/deps/curl/lib/vquic/cf-quiche.c @@ -41,10 +41,11 @@ #include "progress.h" #include "select.h" #include "http1.h" +#include "sockaddr.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-quiche.h" #include "transfer.h" #include "url.h" #include "bufref.h" @@ -75,7 +76,7 @@ void Curl_quiche_ver(char *p, size_t len) struct cf_quiche_ctx { struct cf_quic_ctx q; - struct ssl_peer peer; + struct ssl_peer ssl_peer; struct curl_tls_ctx tls; quiche_conn *qconn; quiche_config *cfg; @@ -106,7 +107,10 @@ static void quiche_debug_log(const char *line, void *argp) static void h3_stream_hash_free(unsigned int id, void *stream); -static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) +static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) { DEBUGASSERT(!ctx->initialized); #ifdef DEBUG_QUICHE @@ -121,6 +125,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) BUFQ_OPT_SOFT_LIMIT); ctx->data_recvd = 0; ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); } static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) @@ -129,7 +134,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) /* quiche freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); - Curl_ssl_peer_cleanup(&ctx->peer); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); vquic_ctx_free(&ctx->q); Curl_uint32_hash_destroy(&ctx->streams); curlx_dyn_free(&ctx->h1hdr); @@ -156,6 +161,7 @@ static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) quiche_config_free(ctx->cfg); ctx->cfg = NULL; } + Curl_ssl_peer_cleanup(&ctx->ssl_peer); } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, @@ -298,7 +304,7 @@ static void cf_quiche_stream_close(struct Curl_cfilter *cf, result = cf_flush_egress(cf, data); if(result) CURL_TRC_CF(data, cf, "[%" PRIu64 "] stream close, flush egress -> %d", - stream->id, result); + stream->id, (int)result); } } @@ -338,7 +344,8 @@ static void cf_quiche_write_hd(struct Curl_cfilter *cf, stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, blen); + "bytes of headers", stream->id, (int)stream->xfer_result, + blen); } } @@ -418,7 +425,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, if(result) { CURL_TRC_CF(x->data, x->cf, "[%" PRIu64 "] on header error %d", - stream->id, result); + stream->id, (int)result); if(!stream->xfer_result) stream->xfer_result = result; } @@ -459,7 +466,7 @@ static void cf_quiche_flush_body(struct Curl_cfilter *cf, Curl_bufq_skip(&ctx->writebuf, blen); if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu bytes" - " of data", stream->id, stream->xfer_result, blen); + " of data", stream->id, (int)stream->xfer_result, blen); } } else @@ -496,9 +503,9 @@ static void cf_quiche_recv_body(struct Curl_cfilter *cf, break; else if(result) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv_body error %d", - stream->id, result); + stream->id, (int)result); failf(data, "[%" PRIu64 "] Error %d in HTTP/3 response body for stream", - stream->id, result); + stream->id, (int)result); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -514,10 +521,14 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, quiche_h3_event *ev) { + enum quiche_h3_event_type type; + if(!stream) return; - switch(quiche_h3_event_type(ev)) { + type = quiche_h3_event_type(ev); + + switch(type) { case QUICHE_H3_EVENT_HEADERS: { struct cb_ctx cb_ctx; stream->resp_got_header = TRUE; @@ -563,7 +574,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, default: CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv, unhandled event %d", - stream->id, quiche_h3_event_type(ev)); + stream->id, (int)type); break; } } @@ -871,7 +882,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", - stream->id, result); + stream->id, (int)result); } else if(!stream->resp_got_header) { failf(data, "HTTP/3 stream %" PRIu64 " was closed cleanly, but before " @@ -922,7 +933,8 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*pnread > 0) ctx->data_recvd += *pnread; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%" - FMT_OFF_T, stream->id, blen, result, *pnread, ctx->data_recvd); + FMT_OFF_T, stream->id, blen, (int)result, *pnread, + ctx->data_recvd); return result; } @@ -1148,7 +1160,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : (uint64_t)~0, len, - result, *pnwritten); + (int)result, *pnwritten); return result; } @@ -1232,7 +1244,7 @@ static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf, body[0] = 'X'; result = cf_quiche_send(cf, data, body, 0, TRUE, &sent); CURL_TRC_CF(data, cf, "[%" PRIu64 "] DONE_SEND -> %d, %zu", - stream->id, result, sent); + stream->id, (int)result, sent); } break; } @@ -1290,7 +1302,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer, &ALPN_SPEC_H3, NULL, NULL, cf, NULL); if(result) return result; @@ -1356,7 +1368,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->ssl_peer); } static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, @@ -1380,7 +1392,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, *done = FALSE; if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } @@ -1498,16 +1510,6 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, return result; } -static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf->ctx) { - bool done; - (void)cf_quiche_shutdown(cf, data, &done); - cf_quiche_ctx_close(cf->ctx); - cf->connected = FALSE; - } -} - static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; @@ -1625,7 +1627,6 @@ struct Curl_cftype Curl_cft_http3 = { 0, cf_quiche_destroy, cf_quiche_connect, - cf_quiche_close, cf_quiche_shutdown, cf_quiche_adjust_pollset, Curl_cf_def_data_pending, @@ -1639,6 +1640,8 @@ struct Curl_cftype Curl_cft_http3 = { CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr) { @@ -1651,14 +1654,15 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_quiche_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + result = cf_quiche_ctx_init(ctx, origin, peer, &conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; @@ -1676,4 +1680,34 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, return result; } +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ + struct cf_quiche_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = cf_quiche_ctx_init(ctx, origin, peer, &cf_at->conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + curlx_safefree(cf); + if(ctx) + cf_quiche_ctx_free(ctx); + } + + return result; +} + #endif diff --git a/deps/curl/lib/vquic/curl_quiche.h b/deps/curl/lib/vquic/cf-quiche.h similarity index 78% rename from deps/curl/lib/vquic/curl_quiche.h rename to deps/curl/lib/vquic/cf-quiche.h index c2c88ddeaf..88d9161dd7 100644 --- a/deps/curl/lib/vquic/curl_quiche.h +++ b/deps/curl/lib/vquic/cf-quiche.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H -#define HEADER_CURL_VQUIC_CURL_QUICHE_H +#ifndef HEADER_CURL_VQUIC_CF_QUICHE_H +#define HEADER_CURL_VQUIC_CF_QUICHE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -37,9 +37,14 @@ void Curl_quiche_ver(char *p, size_t len); CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ +#endif /* HEADER_CURL_VQUIC_CF_QUICHE_H */ diff --git a/deps/curl/lib/vquic/vquic-tls.c b/deps/curl/lib/vquic/vquic-tls.c index d81f2a9e6b..58f139306a 100644 --- a/deps/curl/lib/vquic/vquic-tls.c +++ b/deps/curl/lib/vquic/vquic-tls.c @@ -49,17 +49,12 @@ #include "vtls/vtls_scache.h" #include "vquic/vquic-tls.h" -CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns, - Curl_vquic_tls_ctx_setup *cb_setup, - void *cb_user_data, void *ssl_user_data, - Curl_vquic_session_reuse_cb *session_reuse_cb) +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer) { char tls_id[80]; - CURLcode result; #ifdef USE_OPENSSL Curl_ossl_version(tls_id, sizeof(tls_id)); @@ -71,22 +66,31 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; #endif - (void)session_reuse_cb; - result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC); - if(result) - return result; + if(ssl_peer->origin || ssl_peer->peer) + Curl_ssl_peer_cleanup(ssl_peer); + return Curl_ssl_peer_init(ssl_peer, origin, peer, sslc, + tls_id, TRNSPRT_QUIC); +} +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *ssl_peer, + const struct alpn_spec *alpns, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data, + Curl_vquic_session_reuse_cb *session_reuse_cb) +{ #ifdef USE_OPENSSL - (void)result; - return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns, + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, NULL, ssl_user_data, session_reuse_cb); #elif defined(USE_GNUTLS) - return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns, + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #elif defined(USE_WOLFSSL) - return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns, + return Curl_wssl_ctx_init(&ctx->wssl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #else @@ -178,7 +182,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, NULL) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; else if(!peer->sni && - (wolfSSL_X509_check_ip_asc(cert, peer->hostname, + (wolfSSL_X509_check_ip_asc(cert, peer->origin->hostname, 0) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; wolfSSL_X509_free(cert); diff --git a/deps/curl/lib/vquic/vquic-tls.h b/deps/curl/lib/vquic/vquic-tls.h index c461c1548b..e8d2418b06 100644 --- a/deps/curl/lib/vquic/vquic-tls.h +++ b/deps/curl/lib/vquic/vquic-tls.h @@ -66,13 +66,18 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, struct Curl_ssl_session *scs, bool *do_early_data); +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer); + /** * Initialize the QUIC TLS instances based of the SSL configurations * for the connection filter, transfer and peer. * @param ctx the TLS context to initialize * @param cf the connection filter involved * @param data the transfer involved - * @param peer the peer to be connected to + * @param ssl_peer the SSL peer to be connected to * @param alpns the ALPN specifications to negotiate, may be NULL * @param cb_setup optional callback for early TLS config * @param cb_user_data user_data param for callback @@ -82,7 +87,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, - struct ssl_peer *peer, + struct ssl_peer *ssl_peer, const struct alpn_spec *alpns, Curl_vquic_tls_ctx_setup *cb_setup, void *cb_user_data, diff --git a/deps/curl/lib/vquic/vquic.c b/deps/curl/lib/vquic/vquic.c index 0040ad671f..dfb8346c1a 100644 --- a/deps/curl/lib/vquic/vquic.c +++ b/deps/curl/lib/vquic/vquic.c @@ -41,8 +41,10 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" -#include "vquic/curl_ngtcp2.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-ngtcp2.h" +#include "vquic/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2-proxy.h" +#include "vquic/cf-quiche.h" #include "multiif.h" #include "progress.h" #include "rand.h" @@ -163,12 +165,10 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, ; if(!curlx_sztouz(rv, psent)) { - switch(SOCKERRNO) { - case EAGAIN: -#if EAGAIN != SOCKEWOULDBLOCK - case SOCKEWOULDBLOCK: -#endif + int sockerr = SOCKERRNO; + if(SOCK_EAGAIN(sockerr)) return CURLE_AGAIN; + switch(sockerr) { case SOCKEMSGSIZE: /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ *psent = pktlen; @@ -177,13 +177,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, if(pktlen > gsolen) { /* GSO failure */ infof(data, "sendmsg() returned %zd (errno %d); disable GSO", rv, - SOCKERRNO); + sockerr); qctx->no_gso = TRUE; return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } FALLTHROUGH(); default: - failf(data, "sendmsg() returned %zd (errno %d)", rv, SOCKERRNO); + failf(data, "sendmsg() returned %zd (errno %d)", rv, sockerr); result = CURLE_SEND_ERROR; goto out; } @@ -204,7 +204,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, ; if(!curlx_sztouz(rv, psent)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { result = CURLE_AGAIN; goto out; } @@ -255,9 +255,48 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, VERBOSE(++calls); } out: - CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu)" - " -> %d, sent=%zu", - VQUIC_SEND_METHOD, pktlen, gsolen, calls, result, *psent); + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, calls, (int)result, *psent); + return result; +} + +/* Split QUIC payload by datagram (gso) boundaries when sending over a + * non-UDP lower filter (for example CONNECT-UDP proxy tunnel). */ +static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent, len; + CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); + + *psent = 0; + + /* Send one datagram-sized chunk per call into the lower filter. */ + for(p = pkt; p < end; p += len) { + len = CURLMIN(gsolen, (size_t)(end - p)); + result = Curl_conn_cf_send(cf->next, data, p, len, FALSE, &sent); + /* Report forward progress even if we return CURLE_AGAIN later. */ + VERBOSE(++calls); + /* Preserve lower-filter errors (including CURLE_AGAIN). */ + if(result) + goto out; + + if(sent != len) { + /* We can only send the complete datagram, not parts. */ + result = CURLE_SEND_ERROR; + goto out; + } + *psent += sent; + } + +out: + CURL_TRC_CF(data, cf, + "vquic_cf_send(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", + pktlen, gsolen, calls, (int)result, *psent); return result; } @@ -285,9 +324,9 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } else { result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); - CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=1)" - " -> %d, sent=%zu", - VQUIC_SEND_METHOD, pktlen, gsolen, result, *psent); + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent); } if(!result) qctx->last_io = qctx->last_op; @@ -310,7 +349,22 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len; } - result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + if(qctx->sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + if(gsolen && (blen > gsolen)) { + /* Send one datagram at a time to preserve packet boundaries. */ + result = send_packet_no_gso_cf(cf, data, buf, blen, gsolen, &sent); + } + else { + /* No GSO aggregate to split, regular lower-filter send is enough. */ + result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &sent); + } + } + if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -419,7 +473,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, memset(&mmsg, 0, sizeof(mmsg)); for(i = 0; i < n; ++i) { msg_iov[i].iov_base = bufs[i]; - msg_iov[i].iov_len = (int)sizeof(bufs[i]); + msg_iov[i].iov_len = sizeof(bufs[i]); mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; @@ -432,7 +486,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(mcount == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); goto out; } @@ -446,7 +500,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)", - mcount, SOCKERRNO, errstr); + mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -454,8 +508,10 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, VERBOSE(++calls); for(i = 0; i < mcount; ++i) { /* A zero-length UDP packet is no QUIC packet. Ignore. */ - if(!mmsg[i].msg_len) + if(!mmsg[i].msg_len) { + ++pkts; continue; + } total_nread += mmsg[i].msg_len; gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); @@ -473,8 +529,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -500,11 +557,11 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) { - /* fully initialise this on each call to `recvmsg()`. There seem to + /* fully initialize this on each call to `recvmsg()`. There seem to * operating systems out there that mess with `msg_iov.iov_len`. */ memset(&msg, 0, sizeof(msg)); msg_iov.iov_base = buf; - msg_iov.iov_len = (int)sizeof(buf); + msg_iov.iov_len = sizeof(buf); msg.msg_iov = &msg_iov; msg.msg_iovlen = 1; msg.msg_control = msg_ctrl; @@ -516,7 +573,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(!curlx_sztouz(rc, &nread)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { goto out; } if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { @@ -538,8 +595,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ++calls; /* A 0-length UDP packet is no QUIC packet */ - if(!nread) + if(!nread) { + ++pkts; continue; + } gso_size = vquic_msghdr_get_udp_gro(&msg); if(gso_size == 0) @@ -554,8 +613,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); return result; } @@ -583,7 +643,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(!curlx_sztouz(rv, &nread)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); goto out; } @@ -618,8 +678,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ @@ -699,27 +760,106 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); +#elif defined(USE_QUICHE) + return Curl_cf_quiche_insert_after(cf_at, origin, peer); +#else + (void)cf_at; + (void)origin; + (void)peer; + return CURLE_NOT_BUILT_IN; +#endif +} + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - (void)transport; - DEBUGASSERT(transport == TRNSPRT_QUIC); + (void)transport_peer; + (void)tunnel_transport; + (void)tunnel_peer; + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, addr); + return Curl_cf_ngtcp2_create(pcf, data, origin, peer, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, addr); + return Curl_cf_quiche_create(pcf, data, origin, peer, conn, addr); +#else + *pcf = NULL; + (void)data; + (void)origin; + (void)peer; + (void)conn; + (void)addr; + (void)tunnel_peer; + (void)tunnel_transport; + return CURLE_NOT_BUILT_IN; +#endif +} + +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, origin, peer, + tunnel_peer, tunnel_transport); +#else + (void)cf_at; + (void)data; + (void)origin; + (void)peer; + (void)tunnel_peer; + (void)tunnel_transport; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_create(pcf, data, origin, peer, transport_peer, + conn, addr, + tunnel_peer, tunnel_transport); #else *pcf = NULL; (void)data; (void)conn; (void)addr; + (void)peer; + (void)transport_peer; + (void)tunnel_peer; + (void)tunnel_transport; return CURLE_NOT_BUILT_IN; #endif } +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn, unsigned char transport) @@ -728,19 +868,17 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); return CURLE_QUIC_CONNECT_ERROR; } - if(!(conn->scheme->flags & PROTOPT_SSL)) { + if(!(data->state.origin->scheme->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; } #ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) { + if(conn->socks_proxy.peer) { failf(data, "HTTP/3 is not supported over a SOCKS proxy"); return CURLE_URL_MALFORMAT; } - if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over an HTTP proxy"); - return CURLE_URL_MALFORMAT; - } +#else + (void)conn; #endif return CURLE_OK; diff --git a/deps/curl/lib/vquic/vquic.h b/deps/curl/lib/vquic/vquic.h index 1f0a1ab5e5..5211a9b33a 100644 --- a/deps/curl/lib/vquic/vquic.h +++ b/deps/curl/lib/vquic/vquic.h @@ -39,14 +39,45 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, size_t scidlen, int *qlogfdp); +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_http3; +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +extern struct Curl_cftype Curl_cft_h3_proxy; + +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + #else #define Curl_vquic_init() 1 #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */ diff --git a/deps/curl/lib/vquic/vquic_int.h b/deps/curl/lib/vquic/vquic_int.h index 82bd5b0358..db5183d431 100644 --- a/deps/curl/lib/vquic/vquic_int.h +++ b/deps/curl/lib/vquic/vquic_int.h @@ -90,14 +90,6 @@ void vquic_ctx_set_time(struct cf_quic_ctx *qctx, void vquic_ctx_update_time(struct cf_quic_ctx *qctx, const struct curltime *pnow); -void vquic_push_blocked_pkt(struct Curl_cfilter *cf, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, size_t gsolen); - -CURLcode vquic_send_blocked_pkts(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx); - CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t gsolen); diff --git a/deps/curl/lib/vssh/libssh.c b/deps/curl/lib/vssh/libssh.c index 44094e466c..e4071caa46 100644 --- a/deps/curl/lib/vssh/libssh.c +++ b/deps/curl/lib/vssh/libssh.c @@ -57,6 +57,7 @@ #include "multiif.h" #include "select.h" #include "vssh/vssh.h" +#include "curlx/base64.h" /* for curlx_base64_encode() */ #ifdef HAVE_UNISTD_H #include @@ -109,12 +110,14 @@ static CURLcode sftp_error_to_CURLE(int err) } /* Multiple options: - * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 + * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256] is set with a SHA256 + * hash. + * 2. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 * hash (90s style auth, not sure we should have it here) - * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first + * 3. data->set.ssh_keyfunc callback is set. Then we do trust on first * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE * is returned by it. - * 3. none of the above. We only accept if it is present on known hosts. + * 4. none of the above. We only accept if it is present on known hosts. * * Returns SSH_OK or SSH_ERROR. */ @@ -122,8 +125,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) { int rc; ssh_key pubkey; - size_t hlen; - unsigned char *hash = NULL; + unsigned char *hash_sha256 = NULL; + size_t hlen_sha256; + unsigned char *hash_md5 = NULL; + size_t hlen_md5; char *found_base64 = NULL; char *known_base64 = NULL; int vstate; @@ -139,20 +144,75 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) if(rc != SSH_OK) return rc; + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + const char *pubkey_sha256 = + data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + char *fingerprint_b64 = NULL; + size_t fingerprint_b64_len; + size_t pub_pos = 0; + size_t b64_pos = 0; + + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, + &hash_sha256, &hlen_sha256); + if(rc != SSH_OK || hlen_sha256 != 32) { + failf(data, "Denied establishing ssh session: " + "SHA256 fingerprint not available"); + goto cleanup; + } + + if(curlx_base64_encode((const uint8_t *)hash_sha256, 32, &fingerprint_b64, + &fingerprint_b64_len) != CURLE_OK) { + rc = SSH_ERROR; + goto cleanup; + } + + infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + + /* Find the position of any = padding characters in the public key */ + while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { + pub_pos++; + } + + /* Find the position of any = padding characters in the base64 coded + * hostkey fingerprint */ + while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { + b64_pos++; + } + + /* Before we authenticate we check the hostkey's SHA256 fingerprint + * against a known fingerprint, if available. + */ + if((pub_pos != b64_pos) || + strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { + failf(data, + "Denied establishing ssh session: mismatch SHA256 fingerprint. " + "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); + curlx_free(fingerprint_b64); + rc = SSH_ERROR; + goto cleanup; + } + + curlx_free(fingerprint_b64); + + rc = SSH_OK; + goto cleanup; + } + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - int i; - char md5buffer[33]; const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + char md5buffer[33]; + int i; - rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); - if(rc != SSH_OK || hlen != 16) { + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, + &hash_md5, &hlen_md5); + if(rc != SSH_OK || hlen_md5 != 16) { failf(data, "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } for(i = 0; i < 16; i++) - curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash_md5[i]); infof(data, "SSH MD5 fingerprint: %s", md5buffer); @@ -217,6 +277,8 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) keymatch = CURLKHMATCH_OK; break; case SSH_KNOWN_HOSTS_OTHER: + keymatch = CURLKHMATCH_MISMATCH; + break; case SSH_KNOWN_HOSTS_NOT_FOUND: case SSH_KNOWN_HOSTS_UNKNOWN: case SSH_KNOWN_HOSTS_ERROR: @@ -297,8 +359,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) /* !checksrc! disable BANNEDFUNC 1 */ free(known_base64); /* allocated by libssh, deallocate with system free */ } - if(hash) - ssh_clean_pubkey_hash(&hash); + if(hash_sha256) + ssh_clean_pubkey_hash(&hash_sha256); + if(hash_md5) + ssh_clean_pubkey_hash(&hash_md5); ssh_key_free(pubkey); if(knownhostsentry) { ssh_knownhosts_entry_free(knownhostsentry); @@ -633,7 +697,8 @@ static int myssh_auth_interactive(struct connectdata *conn, if(nprompts != 1) return SSH_ERROR; - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, + Curl_creds_passwd(conn->creds)); if(rc < 0) return SSH_ERROR; @@ -799,7 +864,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, /* Two choices, (1) private key was given on CMD, * (2) use the "default" keys. */ if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - if(sshc->pubkey && !data->set.ssl.key_passwd) { + if(sshc->pubkey && !data->set.ssl.primary.key_passwd) { rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -812,7 +877,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, rc = ssh_pki_import_privkey_file(data-> set.str[STRING_SSH_PRIVATE_KEY], - data->set.ssl.key_passwd, NULL, + data->set.ssl.primary.key_passwd, NULL, NULL, &sshc->privkey); if(rc != SSH_OK) { failf(data, "Could not load private key file %s", @@ -825,7 +890,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, } else { rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, - data->set.ssl.key_passwd); + data->set.ssl.primary.key_passwd); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -920,7 +985,8 @@ static int myssh_in_AUTH_PASS_INIT(struct Curl_easy *data, static int myssh_in_AUTH_PASS(struct Curl_easy *data, struct ssh_conn *sshc) { - int rc = ssh_userauth_password(sshc->ssh_session, NULL, data->conn->passwd); + int rc = ssh_userauth_password(sshc->ssh_session, NULL, + Curl_creds_passwd(data->conn->creds)); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; else if(rc == SSH_AUTH_SUCCESS) { @@ -1807,7 +1873,7 @@ static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, static void sshc_cleanup(struct ssh_conn *sshc) { - if(sshc->initialised) { + if(sshc->initialized) { if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; @@ -1822,8 +1888,8 @@ static void sshc_cleanup(struct ssh_conn *sshc) } /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->scp_session); if(sshc->readdir_tmp) { ssh_string_free_char(sshc->readdir_tmp); @@ -1857,7 +1923,7 @@ static void sshc_cleanup(struct ssh_conn *sshc) curlx_dyn_free(&sshc->readdir_buf); curlx_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); - sshc->initialised = FALSE; + sshc->initialized = FALSE; } } @@ -2378,7 +2444,7 @@ static CURLcode myssh_statemachine(struct Curl_easy *data, if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -2403,7 +2469,7 @@ static CURLcode myssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ @@ -2502,7 +2568,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2); - sshc->initialised = TRUE; + sshc->initialized = TRUE; if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; @@ -2545,13 +2611,13 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) sshc->ssh_session = ssh_new(); if(!sshc->ssh_session) { - failf(data, "Failure initialising ssh session"); + failf(data, "Failure initializing ssh session"); return CURLE_FAILED_INIT; } rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name); + data->state.up.hostname : conn->origin->hostname); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2571,9 +2637,10 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - if(conn->user && conn->user[0] != '\0') { - infof(data, "User: %s", conn->user); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user); + if(Curl_creds_has_user(conn->creds)) { + infof(data, "User: %s", conn->creds->user); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, + conn->creds->user); if(rc != SSH_OK) { failf(data, "Could not set user"); return CURLE_FAILED_INIT; @@ -2595,9 +2662,9 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) } } - if(conn->remote_port) { + if(conn->origin->port) { rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT, - &conn->remote_port); + &conn->origin->port); if(rc != SSH_OK) { failf(data, "Could not set remote port"); return CURLE_FAILED_INIT; @@ -3094,23 +3161,23 @@ void Curl_ssh_version(char *buffer, size_t buflen) * SCP. */ const struct Curl_protocol Curl_protocol_scp = { - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - myssh_pollset, /* proto_pollset */ - myssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - myssh_pollset, /* perform_pollset */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_is_dead */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_pollset, /* proto_pollset */ + myssh_pollset, /* doing_pollset */ + ZERO_NULL, /* domore_pollset */ + myssh_pollset, /* perform_pollset */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ + ZERO_NULL, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ }; /* diff --git a/deps/curl/lib/vssh/libssh2.c b/deps/curl/lib/vssh/libssh2.c index 4e2a72269f..b12d69f317 100644 --- a/deps/curl/lib/vssh/libssh2.c +++ b/deps/curl/lib/vssh/libssh2.c @@ -57,7 +57,7 @@ #include "curlx/fopen.h" #include "vssh/vssh.h" #include "curlx/strparse.h" -#include "curlx/base64.h" /* for base64 encoding/decoding */ +#include "curlx/base64.h" /* for curlx_base64_encode() */ static const char *sftp_libssh2_strerror(unsigned long err) { @@ -104,10 +104,10 @@ static const char *sftp_libssh2_strerror(unsigned long err) case LIBSSH2_FX_QUOTA_EXCEEDED: return "User quota exceeded"; - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; + case LIBSSH2_FX_UNKNOWN_PRINCIPAL: + return "Unknown principal"; - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return "File lock conflict"; case LIBSSH2_FX_DIR_NOT_EMPTY: @@ -148,11 +148,11 @@ static void kbd_callback(const char *name, int name_len, #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; + const char *passwd = Curl_creds_passwd(conn->creds); /* this function must allocate memory that can be freed by libssh2, which uses the LIBSSH2_FREE_FUNC callback */ - responses[0].text = Curl_cstrdup(conn->passwd); - responses[0].length = - responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + responses[0].text = Curl_cstrdup(passwd); + responses[0].length = responses[0].text ? curlx_uztoui(strlen(passwd)) : 0; } (void)prompts; } /* kbd_callback */ @@ -169,7 +169,7 @@ static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) case LIBSSH2_FX_PERMISSION_DENIED: case LIBSSH2_FX_WRITE_PROTECT: - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return CURLE_REMOTE_ACCESS_DENIED; case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: @@ -275,9 +275,11 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) case LIBSSH2_HOSTKEY_TYPE_RSA: keytype = CURLKHTYPE_RSA; break; - case LIBSSH2_HOSTKEY_TYPE_DSS: +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ keytype = CURLKHTYPE_DSS; break; +#endif #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keytype = CURLKHTYPE_ECDSA; @@ -305,136 +307,154 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) static CURLcode ssh_knownhost(struct Curl_easy *data, struct ssh_conn *sshc) { + struct connectdata *conn = data->conn; + struct libssh2_knownhost *host = NULL; + const char *remotekey = NULL; + int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; + int keybit = 0; int sshkeytype = 0; size_t keylen = 0; int rc = 0; CURLcode result = CURLE_OK; - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we are asked to verify the host against a file */ - struct connectdata *conn = data->conn; - struct libssh2_knownhost *host = NULL; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &sshkeytype); - int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; - int keybit = 0; + if(!data->set.str[STRING_SSH_KNOWNHOSTS]) { + infof(data, "SSH: no knownhosts file configured"); + return CURLE_OK; + } - if(remotekey) { - /* - * A subject to figure out is what hostname we need to pass in here. - * What hostname does OpenSSH store in its file if an IDN name is - * used? - */ - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - switch(sshkeytype) { - case LIBSSH2_HOSTKEY_TYPE_RSA: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; - break; - case LIBSSH2_HOSTKEY_TYPE_DSS: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; - break; - case LIBSSH2_HOSTKEY_TYPE_ED25519: - keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; - break; - default: - infof(data, "unsupported key type, cannot check knownhosts"); - keybit = 0; - break; + remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &sshkeytype); + if(remotekey) { + /* + * A subject to figure out is what hostname we need to pass in here. + * What hostname does OpenSSH store in its file if an IDN name is + * used? + */ + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + switch(sshkeytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; +#endif + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + break; + default: + infof(data, "SSH: unsupported host key type for knownhosts check"); + keybit = 0; + break; + } + if(!keybit) + /* no check means failure! */ + rc = CURLKHSTAT_REJECT; + else { + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->origin->hostname, + (conn->origin->port != PORT_SSH) ? + conn->origin->port : -1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); + + infof(data, "SSH: host check %d, key: %s", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? + host->key : ""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = convert_ssh2_keytype(sshkeytype); + knownkeyp = &knownkey; } - if(!keybit) - /* no check means failure! */ - rc = CURLKHSTAT_REJECT; - else { - keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->host.name, - (conn->remote_port != PORT_SSH) ? - conn->remote_port : -1, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); - - infof(data, "SSH host check: %d, key: %s", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? - host->key : ""); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = convert_ssh2_keytype(sshkeytype); - knownkeyp = &knownkey; - } - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = convert_ssh2_keytype(sshkeytype); + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = convert_ssh2_keytype(sshkeytype); - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; - /* Ask the callback how to behave */ - Curl_set_in_callback(data, TRUE); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); - } + /* Ask the callback how to behave */ + Curl_set_in_callback(data, TRUE); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, FALSE); } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; + } + else { + /* no remotekey means failure! */ + infof(data, "SSH: host offers no public key"); + rc = CURLKHSTAT_REJECT; + } - switch(rc) { - default: /* unknown return codes is the same as reject */ - case CURLKHSTAT_REJECT: - myssh_to(data, sshc, SSH_SESSION_FREE); - FALLTHROUGH(); - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE_REPLACE: - /* remove old host+key that does not match */ - if(host) - libssh2_knownhost_del(sshc->kh, host); - FALLTHROUGH(); - case CURLKHSTAT_FINE: - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + switch(rc) { + default: /* unknown return codes is the same as reject */ + case CURLKHSTAT_REJECT: + infof(data, "SSH: knownhost check failed"); + myssh_to(data, sshc, SSH_SESSION_FREE); + FALLTHROUGH(); + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE_REPLACE: + /* remove old host+key that does not match */ + if(host) + libssh2_knownhost_del(sshc->kh, host); + FALLTHROUGH(); + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + int addrc; + const char *hostbuf; + char *hostport = NULL; + if(conn->origin->port != PORT_SSH) { + hostbuf = hostport = curl_maprintf("[%s]:%u", conn->origin->hostname, + conn->origin->port); + if(!hostbuf) + infof(data, "WARNING: failed allocating buffer for [host]:port"); + } + else + hostbuf = conn->origin->hostname; + if(hostbuf) { /* the found host+key did not match but has been told to be fine anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); + addrc = libssh2_knownhost_addc(sshc->kh, hostbuf, NULL, + remotekey, keylen, NULL, 0, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, NULL); if(addrc) - infof(data, "WARNING: adding the known host %s failed", - conn->host.name); + infof(data, "WARNING: adding the known host %s failed", hostbuf); else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || rc == CURLKHSTAT_FINE_REPLACE) { /* now we write the entire in-memory list of known hosts to the @@ -449,8 +469,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, } } } - break; + curlx_free(hostport); } + else + infof(data, "SSH: knownhost entry matches host key"); + break; } return result; } @@ -461,11 +484,6 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; - infof(data, "SSH MD5 public key: %s", - pubkey_md5 != NULL ? pubkey_md5 : "NULL"); - infof(data, "SSH SHA256 public key: %s", - pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); - if(pubkey_sha256) { const char *fingerprint = NULL; char *fingerprint_b64 = NULL; @@ -473,6 +491,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, size_t pub_pos = 0; size_t b64_pos = 0; + infof(data, "SSH: SHA256 public key '%s'", pubkey_sha256); /* The fingerprint points to static storage (!), do not free() it. */ fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_SHA256); @@ -498,7 +517,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + infof(data, "SSH: SHA256 fingerprint '%s'", fingerprint_b64); /* Find the position of any = padding characters in the public key */ while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { @@ -526,13 +545,14 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, curlx_free(fingerprint_b64); - infof(data, "SHA256 checksum match"); + infof(data, "SSH: SHA256 checksum match"); } if(pubkey_md5) { char md5buffer[33]; const char *fingerprint; + infof(data, "SSH: MD5 public key '%s'", pubkey_md5); fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_MD5); @@ -544,7 +564,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, (unsigned char)fingerprint[i]); } - infof(data, "SSH MD5 fingerprint: %s", md5buffer); + infof(data, "SSH: MD5 fingerprint '%s'", md5buffer); } /* This does NOT verify the length of 'pubkey_md5' separately, which @@ -563,7 +583,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "MD5 checksum match"); + infof(data, "SSH: MD5 checksum match"); } if(!pubkey_md5 && !pubkey_sha256) { @@ -582,8 +602,10 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); if(rc != CURLKHMATCH_OK) { myssh_to(data, sshc, SSH_SESSION_FREE); + failf(data, "SSH: callback failed host public key verification"); return CURLE_PEER_FAILED_VERIFICATION; } + infof(data, "SSH: verified public key via callback"); } else { myssh_to(data, sshc, SSH_SESSION_FREE); @@ -592,6 +614,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_OK; } else { + CURL_TRC_SSH(data, "no host key checksum given, checking knownhosts"); return ssh_knownhost(data, sshc); } } @@ -616,7 +639,9 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, static const char hostkey_method_ssh_ecdsa_256[] = "ecdsa-sha2-nistp256"; static const char hostkey_method_ssh_rsa_all[] = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS static const char hostkey_method_ssh_dss[] = "ssh-dss"; +#endif bool found = FALSE; if(sshc->kh && @@ -636,22 +661,22 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, const char *p; const char *kh_name_end = strstr(store->name, "]:"); if(!kh_name_end) { - infof(data, "Invalid host pattern %s in %s", + infof(data, "SSH: invalid host pattern %s in %s", store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); continue; } p = kh_name_end + 2; /* start of port number */ if(!curlx_str_number(&p, &port, 0xffff) && - (kh_name_end && (port == conn->remote_port))) { + (kh_name_end && (port == conn->origin->port))) { kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); - if(strncmp(store->name + 1, - conn->host.name, kh_name_size) == 0) { + if(!strncmp(store->name + 1, conn->origin->hostname, + kh_name_size)) { found = TRUE; break; } } } - else if(strcmp(store->name, conn->host.name) == 0) { + else if(!strcmp(store->name, conn->origin->hostname)) { found = TRUE; break; } @@ -666,8 +691,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, if(found) { int rc; const char *hostkey_method = NULL; - infof(data, "Found host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + infof(data, "SSH: found host '%s' in '%s'", + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { case LIBSSH2_KNOWNHOST_KEY_ED25519: @@ -685,19 +710,21 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, case LIBSSH2_KNOWNHOST_KEY_SSHRSA: hostkey_method = hostkey_method_ssh_rsa_all; break; - case LIBSSH2_KNOWNHOST_KEY_SSHDSS: +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: /* deprecated upstream */ hostkey_method = hostkey_method_ssh_dss; break; +#endif case LIBSSH2_KNOWNHOST_KEY_RSA1: failf(data, "Found host key type RSA1 which is not supported"); return CURLE_SSH; default: - failf(data, "Unknown host key type: %i", + failf(data, "Unknown host key type: %d", (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); return CURLE_SSH; } - infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); + infof(data, "SSH: set '%s' as hostkey type", hostkey_method); rc = libssh2_session_method_pref(sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if(rc) { @@ -709,8 +736,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } } else { - infof(data, "Did not find host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + infof(data, "SSH: did not find host '%s' in '%s'", + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); } } @@ -778,7 +805,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, cp = strchr(cmd, ' '); if(!cp) { failf(data, "Syntax error command '%s', missing parameter", cmd); - return result; + return CURLE_QUOTE_ERROR; } /* @@ -1077,7 +1104,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, sshc->authed = FALSE; if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { bool out_of_memory = FALSE; sshc->rsa_pub = sshc->rsa = NULL; @@ -1146,13 +1173,13 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - sshc->passphrase = data->set.ssl.key_passwd; + sshc->passphrase = data->set.ssl.primary.key_passwd; if(!sshc->passphrase) sshc->passphrase = ""; if(sshc->rsa_pub) - infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); - infof(data, "Using SSH private key file '%s'", sshc->rsa); + infof(data, "SSH: trying public key file '%s'", sshc->rsa_pub); + infof(data, "SSH: trying private key file '%s'", sshc->rsa); myssh_to(data, sshc, SSH_AUTH_PKEY); } @@ -1180,7 +1207,7 @@ static CURLcode sftp_quote_stat(struct Curl_easy *data, sshc->acceptfail = TRUE; } - if(!!strncmp(cmd, "chmod", 5)) { + if(strncmp(cmd, "chmod", 5)) { /* Since chown and chgrp only set owner OR group but libssh2 wants to set * them both at once, we need to obtain the current ownership first. This * takes an extra protocol round trip. @@ -1354,7 +1381,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, if(data->req.size == 0) { /* no data to transfer */ Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); + infof(data, "SSH: file already completely downloaded"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1496,15 +1523,15 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, * Therefore always specify it here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); sshc->authlist = libssh2_userauth_list(sshc->ssh_session, - conn->user, - curlx_uztoui(strlen(conn->user))); + user, curlx_uztoui(strlen(user))); if(!sshc->authlist) { int rc; if(libssh2_userauth_authenticated(sshc->ssh_session)) { sshc->authed = TRUE; - infof(data, "SSH user accepted with no authentication"); + infof(data, "SSH: user accepted with no authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1515,7 +1542,7 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, myssh_to(data, sshc, SSH_SESSION_FREE); return libssh2_session_error_to_CURLE(rc); } - infof(data, "SSH authentication methods available: %s", sshc->authlist); + infof(data, "SSH: host offers authentication via: %s", sshc->authlist); myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); return CURLE_OK; @@ -1527,11 +1554,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, /* The function below checks if the files exists, no need to stat() here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, + curlx_uztoui(strlen(user)), sshc->rsa_pub, sshc->rsa, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -1542,7 +1569,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized SSH public key authentication"); + infof(data, "SSH: authenticated via publickey"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1556,7 +1583,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, else { (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); } - infof(data, "SSH public key authentication failed: %s", err_msg); + infof(data, "SSH: publickey authentication denied: %s", err_msg); myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return CURLE_OK; @@ -1566,7 +1593,7 @@ static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && - (strstr(sshc->authlist, "password") != NULL)) { + strstr(sshc->authlist, "password")) { myssh_to(data, sshc, SSH_AUTH_PASS); } else { @@ -1579,18 +1606,20 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, struct ssh_conn *sshc) { struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); + const char *passwd = Curl_creds_passwd(conn->creds); int rc = - libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - curlx_uztoui(strlen(conn->user)), - conn->passwd, - curlx_uztoui(strlen(conn->passwd)), + libssh2_userauth_password_ex(sshc->ssh_session, user, + curlx_uztoui(strlen(user)), + passwd, + curlx_uztoui(strlen(passwd)), NULL); if(rc == LIBSSH2_ERROR_EAGAIN) { return CURLE_AGAIN; } if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized password authentication"); + infof(data, "SSH: initialized password authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1603,7 +1632,7 @@ static CURLcode ssh_state_auth_host_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && - (strstr(sshc->authlist, "hostbased") != NULL)) { + strstr(sshc->authlist, "hostbased")) { myssh_to(data, sshc, SSH_AUTH_HOST); } else { @@ -1617,15 +1646,16 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, { int rc = 0; if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { + infof(data, "SSH: trying publickey authentication via agent"); /* Connect to the ssh-agent */ /* The agent could be shared by a curl thread i believe but nothing obvious as keys can be added/removed at any time */ if(!sshc->ssh_agent) { sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); if(!sshc->ssh_agent) { - infof(data, "Could not create agent object"); + infof(data, "SSH: could not create agent object"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; @@ -1636,7 +1666,7 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure connecting to agent"); + infof(data, "SSH: failure connecting to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1656,7 +1686,7 @@ static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1679,8 +1709,11 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, return CURLE_AGAIN; if(rc == 0) { - struct connectdata *conn = data->conn; - rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + CURL_TRC_SSH(data, "[SSH_AUTH_AGENT_LIST] auth user '%s' for key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); + rc = libssh2_agent_userauth(sshc->ssh_agent, + Curl_creds_user(data->conn->creds), sshc->sshagent_identity); if(rc < 0) { @@ -1694,13 +1727,15 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, } if(rc < 0) - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); else if(rc == 1) - infof(data, "No identity would match"); + infof(data, "SSH: no agent identity would match"); if(rc == LIBSSH2_ERROR_NONE) { sshc->authed = TRUE; - infof(data, "Agent based authentication successful"); + infof(data, "SSH: agent authenticated user '%s' with key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1713,7 +1748,7 @@ static CURLcode ssh_state_auth_key_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) && - (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { + strstr(sshc->authlist, "keyboard-interactive")) { myssh_to(data, sshc, SSH_AUTH_KEY); } else { @@ -1727,18 +1762,17 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, { /* Authentication failed. Continue with keyboard-interactive now. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, curlx_uztoui(strlen(user)), &kbd_callback); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized keyboard interactive authentication"); + infof(data, "SSH: initialized keyboard interactive authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1758,7 +1792,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, /* * At this point we have an authenticated ssh session. */ - infof(data, "Authentication complete"); + infof(data, "SSH: authentication complete"); Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ @@ -1769,7 +1803,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, myssh_to(data, sshc, SSH_SFTP_INIT); return CURLE_OK; } - infof(data, "SSH CONNECT phase done"); + infof(data, "SSH: connection established"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1863,7 +1897,7 @@ static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data, } if(data->set.quote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.quote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -1877,7 +1911,7 @@ static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data, struct ssh_conn *sshc) { if(data->set.postquote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.postquote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -2585,12 +2619,12 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, } /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); - DEBUGASSERT(sshc->kh == NULL); - DEBUGASSERT(sshc->ssh_agent == NULL); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->ssh_channel); + DEBUGASSERT(!sshc->sftp_session); + DEBUGASSERT(!sshc->sftp_handle); + DEBUGASSERT(!sshc->kh); + DEBUGASSERT(!sshc->ssh_agent); curlx_safefree(sshc->rsa_pub); curlx_safefree(sshc->rsa); @@ -2692,7 +2726,7 @@ static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, sshc->slash_pos = strchr(sshc->slash_pos, '/'); if(sshc->slash_pos) { *sshc->slash_pos = 0; - infof(data, "Creating directory '%s'", sshp->path); + infof(data, "SFTP: creating directory '%s'", sshp->path); myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); return CURLE_OK; } @@ -2817,8 +2851,7 @@ static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, - "Failed to send libssh2 channel EOF: %d %s", + infof(data, "Failed to send libssh2 channel EOF: %d %s", rc, err_msg); } } @@ -2837,8 +2870,7 @@ static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", - rc, err_msg); + infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); @@ -2857,8 +2889,7 @@ static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", - rc, err_msg); + infof(data, "Channel failed to close: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); @@ -2877,9 +2908,7 @@ static CURLcode ssh_state_scp_channel_free( char *err_msg = NULL; (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - infof(data, - "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); } sshc->ssh_channel = NULL; } @@ -3162,7 +3191,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = CURLE_OK; } CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -3188,7 +3217,7 @@ static CURLcode ssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ @@ -3445,16 +3474,16 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) break; } if(crypto_str) - infof(data, "libssh2 cryptography backend: %s", crypto_str); + infof(data, "SSH: libssh2 cryptography backend: %s", crypto_str); } #endif if(!sshc) return CURLE_FAILED_INIT; - infof(data, "User: '%s'", conn->user); + infof(data, "SSH: user '%s'", Curl_creds_user(conn->creds)); #ifdef CURL_LIBSSH2_DEBUG - infof(data, "Password: %s", conn->passwd); + infof(data, "SSH: password %s", Curl_creds_passwd(conn->creds)); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ @@ -3465,7 +3494,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) my_libssh2_realloc, data); if(!sshc->ssh_session) { - failf(data, "Failure initialising ssh session"); + failf(data, "Failure initializing ssh session"); return CURLE_FAILED_INIT; } @@ -3492,7 +3521,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) */ #if LIBSSH2_VERSION_NUM >= 0x010b01 - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); #if defined(__clang__) && __clang_major__ >= 16 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-function-type-strict" @@ -3526,7 +3555,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sshrecv.recvptr = ssh_tls_recv; sshsend.sendptr = ssh_tls_send; - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); libssh2_session_callback_set(sshc->ssh_session, LIBSSH2_CALLBACK_RECV, sshrecv.recvp); libssh2_session_callback_set(sshc->ssh_session, @@ -3551,7 +3580,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(data->set.ssh_compression && libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) { - infof(data, "Failed to enable compression for ssh session"); + infof(data, "SSH: failed to enable compression for session"); } if(data->set.str[STRING_SSH_KNOWNHOSTS]) { @@ -3568,14 +3597,14 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) data->set.str[STRING_SSH_KNOWNHOSTS], LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(rc < 0) - infof(data, "Failed to read known hosts from %s", + infof(data, "SSH: failed to read known hosts from %s", data->set.str[STRING_SSH_KNOWNHOSTS]); } #ifdef CURL_LIBSSH2_DEBUG libssh2_trace(sshc->ssh_session, ~0); - infof(data, "SSH socket: %d", (int)sock); -#endif /* CURL_LIBSSH2_DEBUG */ + infof(data, "SSH: socket %d", (int)sock); +#endif myssh_to(data, sshc, SSH_INIT); @@ -3823,7 +3852,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, CURL_TRC_SSH(data, "DISCONNECT starts now"); myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = ssh_block_statemach(data, sshc, sshp, TRUE); - CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result); + CURL_TRC_SSH(data, "DISCONNECT is done -> %d", (int)result); } sshc_cleanup(sshc, data, TRUE); } diff --git a/deps/curl/lib/vssh/ssh.h b/deps/curl/lib/vssh/ssh.h index 44e72ab802..de76c8a253 100644 --- a/deps/curl/lib/vssh/ssh.h +++ b/deps/curl/lib/vssh/ssh.h @@ -195,7 +195,7 @@ struct ssh_conn { const char *readdir_filename; /* points within readdir_attrs */ const char *readdir_longentry; char *readdir_tmp; - BIT(initialised); + BIT(initialized); #elif defined(USE_LIBSSH2) LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ @@ -229,9 +229,9 @@ struct ssh_conn { /* Feature detection based on version numbers to better work with non-configure platforms */ -#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208) -#error "SCP/SFTP protocols require libssh2 1.2.8 or later" -/* 1.2.8 was released on April 5 2011 */ +#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010900) +#error "SCP/SFTP protocols require libssh2 1.9.0 or greater" +/* 1.9.0 was released on June 20 2019 */ #endif #endif /* USE_LIBSSH2 */ diff --git a/deps/curl/lib/vtls/apple.c b/deps/curl/lib/vtls/apple.c index e28a40cc0e..2e132f2977 100644 --- a/deps/curl/lib/vtls/apple.c +++ b/deps/curl/lib/vtls/apple.c @@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, if(conn_config->verifyhost) { host_str = CFStringCreateWithCString(NULL, - peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); + peer->sni ? peer->sni : peer->origin->hostname, kCFStringEncodingUTF8); if(!host_str) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -208,7 +208,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustSetOCSPResponse(trust, ocspdata); CFRelease(ocspdata); if(status != noErr) { - failf(data, "Apple SecTrust: failed to set OCSP response: %i", + failf(data, "Apple SecTrust: failed to set OCSP response: %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; goto out; @@ -238,10 +238,8 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, err_desc = curlx_malloc(size + 1); if(err_desc) { if(!CFStringGetCString(error_ref, err_desc, size, - kCFStringEncodingUTF8)) { - curlx_free(err_desc); - err_desc = NULL; - } + kCFStringEncodingUTF8)) + curlx_safefree(err_desc); } } infof(data, "Apple SecTrust failure %ld%s%s", code, @@ -256,7 +254,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustEvaluate(trust, &sec_result); if(status != noErr) { - failf(data, "Apple SecTrust verification failed: error %i", (int)status); + failf(data, "Apple SecTrust verification failed: error %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; } else if((sec_result == kSecTrustResultUnspecified) || diff --git a/deps/curl/lib/vtls/cipher_suite.c b/deps/curl/lib/vtls/cipher_suite.c index 1fc6e9f8c6..197055fc0d 100644 --- a/deps/curl/lib/vtls/cipher_suite.c +++ b/deps/curl/lib/vtls/cipher_suite.c @@ -639,7 +639,7 @@ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) if(cs_len > 0 && cs_str_to_zip(cs_str, cs_len, zip) == 0) { for(i = 0; i < CS_LIST_LEN; i++) { - if(memcmp(cs_list[i].zip, zip, sizeof(zip)) == 0) + if(!memcmp(cs_list[i].zip, zip, sizeof(zip))) return cs_list[i].id; } } diff --git a/deps/curl/lib/vtls/gtls.c b/deps/curl/lib/vtls/gtls.c index 9b6a4fab2e..1a7a4a1e5b 100644 --- a/deps/curl/lib/vtls/gtls.c +++ b/deps/curl/lib/vtls/gtls.c @@ -26,7 +26,7 @@ * but vtls.c should ever call or use these functions. * * Note: do not use the GnuTLS' *_t variable type names in this source code, - * since they were not present in 1.0.X. + * since they were not present in 1.0.x. */ #include "curl_setup.h" @@ -37,6 +37,7 @@ #include #include #include +#include #include "urldata.h" #include "curl_trc.h" @@ -71,7 +72,7 @@ static void tls_log_func(int level, const char *str) #endif #undef CURL_GNUTLS_EARLY_DATA -#if GNUTLS_VERSION_NUMBER >= 0x03060d +#if GNUTLS_VERSION_NUMBER >= 0x030702 #define CURL_GNUTLS_EARLY_DATA #endif @@ -94,7 +95,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) DEBUGASSERT(data); result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -127,7 +128,8 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "gtls_pull(len=%zu) -> %d, %zu", blen, (int)result, + nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -203,12 +205,12 @@ static gnutls_datum_t load_file(const char *file) f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0) + if(fseek(f, 0, SEEK_END)) goto out; filelen = ftell(f); if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) goto out; - if(fseek(f, 0, SEEK_SET) != 0) + if(fseek(f, 0, SEEK_SET)) goto out; ptr = curlx_malloc((size_t)filelen); if(!ptr) @@ -322,12 +324,12 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) "+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ "%DISABLE_TLS13_COMPAT_MODE" -static CURLcode -gnutls_set_ssl_version_min_max(struct Curl_easy *data, - struct ssl_peer *peer, - struct ssl_primary_config *conn_config, - const char **prioritylist, - bool tls13support) +static CURLcode gnutls_set_ssl_version_min_max( + struct Curl_easy *data, + struct ssl_peer *peer, + struct ssl_primary_config *conn_config, + const char **prioritylist, + bool tls13support) { long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; @@ -408,7 +410,8 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, rc = gnutls_certificate_allocate_credentials(&shared->creds); if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); + failf(data, "gnutls_certificate_allocate_credentials() failed: %s", + gnutls_strerror(rc)); curlx_free(shared); return CURLE_SSL_CONNECT_ERROR; } @@ -692,6 +695,24 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, } #ifdef CURL_GNUTLS_EARLY_DATA +static int gtls_get_ietf_proto(gnutls_session_t session) +{ + switch(gnutls_protocol_get_version(session)) { + case GNUTLS_SSL3: + return CURL_IETF_PROTO_SSL3; + case GNUTLS_TLS1_0: + return CURL_IETF_PROTO_TLS1; + case GNUTLS_TLS1_1: + return CURL_IETF_PROTO_TLS1_1; + case GNUTLS_TLS1_2: + return CURL_IETF_PROTO_TLS1_2; + case GNUTLS_TLS1_3: + return CURL_IETF_PROTO_TLS1_3; + default: + return CURL_IETF_PROTO_UNKNOWN; + } +} + CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -740,7 +761,7 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } result = Curl_ssl_session_create2(sdata, sdata_len, - Curl_glts_get_ietf_proto(session), + gtls_get_ietf_proto(session), alpn, valid_until, earlydata_max, qtp_clone, quic_tp_len, &sc_session); @@ -753,24 +774,6 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } #endif -int Curl_glts_get_ietf_proto(gnutls_session_t session) -{ - switch(gnutls_protocol_get_version(session)) { - case GNUTLS_SSL3: - return CURL_IETF_PROTO_SSL3; - case GNUTLS_TLS1_0: - return CURL_IETF_PROTO_TLS1; - case GNUTLS_TLS1_1: - return CURL_IETF_PROTO_TLS1_1; - case GNUTLS_TLS1_2: - return CURL_IETF_PROTO_TLS1_2; - case GNUTLS_TLS1_3: - return CURL_IETF_PROTO_TLS1_3; - default: - return CURL_IETF_PROTO_UNKNOWN; - } -} - #ifdef CURL_GNUTLS_EARLY_DATA static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -839,7 +842,7 @@ static CURLcode gtls_set_priority(struct Curl_cfilter *cf, if((conn_config->cipher_list[0] == '+') || (conn_config->cipher_list[0] == '-') || (conn_config->cipher_list[0] == '!')) { - /* add it to out own */ + /* add it to our own */ if(!curlx_dyn_len(&buf)) { /* not added yet */ result = curlx_dyn_add(&buf, priority); if(result) @@ -995,10 +998,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; } - if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) { + if(ssl_config->primary.cert_type && + curl_strequal(ssl_config->primary.cert_type, "P12")) { rc = gnutls_certificate_set_x509_simple_pkcs12_file( gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER, - ssl_config->key_passwd ? ssl_config->key_passwd : ""); + ssl_config->primary.key_passwd ? ssl_config->primary.key_passwd : ""); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 potentially-encrypted key or certificate " @@ -1016,14 +1020,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, rc = gnutls_certificate_set_x509_key_file2( gtls->shared_creds->creds, config->clientcert, - ssl_config->key ? ssl_config->key : config->clientcert, - gnutls_do_file_type(ssl_config->cert_type), - ssl_config->key_passwd, + ssl_config->primary.key ? ssl_config->primary.key : + config->clientcert, + gnutls_do_file_type(ssl_config->primary.cert_type), + ssl_config->primary.key_passwd, supported_key_encryption_algorithms); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 %skey file: %s", - ssl_config->key_passwd ? "potentially-encrypted " : "", + ssl_config->primary.key_passwd ? "potentially-encrypted " : "", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } @@ -1361,11 +1366,12 @@ static void gtls_msg_verify_result(struct Curl_easy *data, if(!was_verified) { if(needs_verified) { failf(data, "SSL: certificate subject name (%s) does not match " - "target hostname '%s'", certname, peer->dispname); + "target hostname '%s'", certname, + peer->origin->user_hostname); } else infof(data, " common name: %s (does not match '%s')", - certname, peer->dispname); + certname, peer->origin->user_hostname); } else infof(data, " common name: %s (matched)", certname); @@ -1425,7 +1431,10 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, { gnutls_ocsp_resp_t ocsp_resp = NULL; gnutls_datum_t status_request; - gnutls_ocsp_cert_status_t status; + gnutls_certificate_credentials_t creds = NULL; + gnutls_x509_trust_list_t tlist = NULL; + unsigned int verify_status = 0; + gnutls_ocsp_cert_status_t status = GNUTLS_OCSP_CERT_UNKNOWN; gnutls_x509_crl_reason_t reason; CURLcode result = CURLE_OK; int rc; @@ -1457,6 +1466,20 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } + if(!gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, (void **)&creds)) + gnutls_certificate_get_trust_list(creds, &tlist); + if(!tlist) { + failf(data, "OCSP response signature verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + rc = gnutls_ocsp_resp_verify(ocsp_resp, tlist, &verify_status, 0); + if(rc < 0 || verify_status) { + failf(data, "OCSP response signature verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, &status, NULL, NULL, NULL, &reason); @@ -1555,21 +1578,75 @@ static CURLcode gtls_chain_get_der(struct Curl_cfilter *cf, *pder_len = (size_t)chain->certs[i].size; return CURLE_OK; } +#endif /* USE_APPLE_SECTRUST */ -static CURLcode glts_apple_verify(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - struct gtls_cert_chain *chain, - bool *pverified) +/* This function verifies the peer's certificate and returns CURLE_OK on + success or an appropriate CURLcode on error. The certificate verification + status bitmask (trusted, invalid etc.) is stored in + ssl_config->certverifyresult as one or more gnutls_certificate_status_t + enumerated elements bitwise or'd. */ +static CURLcode gtls_verify_cert(struct Curl_easy *data, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + gnutls_session_t session, + struct Curl_cfilter *cf, + struct ssl_peer *peer, + struct gtls_cert_chain *chain) { - CURLcode result; + bool verified = FALSE; + unsigned int verify_status = 0; + long * const certverifyresult = &ssl_config->certverifyresult; + int rc = gnutls_certificate_verify_peers2(session, &verify_status); + if(rc < 0) { + failf(data, "server cert verify failed: %d", rc); + *certverifyresult = rc; + return CURLE_SSL_CONNECT_ERROR; + } + *certverifyresult = verify_status; + verified = !(verify_status & GNUTLS_CERT_INVALID); + if(verified) + infof(data, " SSL certificate verified by GnuTLS"); - result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, - gtls_chain_get_der, chain, NULL, 0); - *pverified = !result; - return result; +#ifdef USE_APPLE_SECTRUST + if(!verified && ssl_config->native_ca_store) { + CURLcode result = + Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, + gtls_chain_get_der, chain, NULL, 0); + if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) + return result; /* unexpected error */ + verified = !result; + if(verified) { + infof(data, "SSL certificate verified via Apple SecTrust."); + *certverifyresult = 0; + } + } +#else + (void)cf; + (void)peer; + (void)chain; +#endif + + if(!verified) { + /* verify_status is a bitmask of gnutls_certificate_status bits */ + const char *cause = "certificate error, no details available"; + if(verify_status & GNUTLS_CERT_EXPIRED) + cause = "certificate has expired"; + else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) + cause = "certificate signer not trusted"; + else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) + cause = "certificate uses insecure algorithm"; + else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) + cause = "attached OCSP status response is invalid"; + failf(data, "SSL certificate verification failed: %s. (CAfile: %s " + "CRLfile: %s)", cause, + config->CAfile ? config->CAfile : "none", + ssl_config->primary.CRLfile ? + ssl_config->primary.CRLfile : "none"); + + return CURLE_PEER_FAILED_VERIFICATION; + } + return CURLE_OK; } -#endif /* USE_APPLE_SECTRUST */ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1643,58 +1720,10 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } if(config->verifypeer) { - bool verified = FALSE; - unsigned int verify_status = 0; - /* This function tries to verify the peer's certificate and return - its status (trusted, invalid etc.). The value of status should be - one or more of the gnutls_certificate_status_t enumerated elements - bitwise or'd. To avoid denial of service attacks some default - upper limits regarding the certificate key size and chain size - are set. To override them use - gnutls_certificate_set_verify_limits(). */ - rc = gnutls_certificate_verify_peers2(session, &verify_status); - if(rc < 0) { - failf(data, "server cert verify failed: %d", rc); - *certverifyresult = rc; - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - *certverifyresult = verify_status; - verified = !(verify_status & GNUTLS_CERT_INVALID); - if(verified) - infof(data, " SSL certificate verified by GnuTLS"); - -#ifdef USE_APPLE_SECTRUST - if(!verified && ssl_config->native_ca_store) { - result = glts_apple_verify(cf, data, peer, &chain, &verified); - if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) - goto out; /* unexpected error */ - if(verified) { - infof(data, "SSL certificate verified via Apple SecTrust."); - *certverifyresult = 0; - } - } -#endif - - if(!verified) { - /* verify_status is a bitmask of gnutls_certificate_status bits */ - const char *cause = "certificate error, no details available"; - if(verify_status & GNUTLS_CERT_EXPIRED) - cause = "certificate has expired"; - else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) - cause = "certificate signer not trusted"; - else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) - cause = "certificate uses insecure algorithm"; - else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) - cause = "attached OCSP status response is invalid"; - failf(data, "SSL certificate verification failed: %s. (CAfile: %s " - "CRLfile: %s)", cause, - config->CAfile ? config->CAfile : "none", - ssl_config->primary.CRLfile ? - ssl_config->primary.CRLfile : "none"); - result = CURLE_PEER_FAILED_VERIFICATION; + result = gtls_verify_cert(data, config, ssl_config, session, + cf, peer, &chain); + if(result) goto out; - } } else infof(data, " SSL certificate verification SKIPPED"); @@ -1791,17 +1820,22 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } issuerp = load_file(config->issuercert); rc = gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - if(!rc) - rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); unload_file(issuerp); + if(rc) { + failf(data, "failed to import issuer certificate (%s) (Issuer Cert: %s)", + gnutls_strerror(rc), config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - config->issuercert ? config->issuercert : "none"); + failf(data, "server certificate issuer check failed (Issuer Cert: %s)", + config->issuercert); result = CURLE_SSL_ISSUER_ERROR; goto out; } infof(data, " SSL certificate issuer check OK (Issuer Cert: %s)", - config->issuercert ? config->issuercert : "none"); + config->issuercert); } /* This function checks if the given certificate's subject matches the @@ -1815,7 +1849,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, IP addresses) */ rc = (int)gnutls_x509_crt_check_hostname(x509_cert, peer->sni ? peer->sni : - peer->hostname); + peer->origin->hostname); result = (!rc && config->verifyhost) ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); @@ -2014,7 +2048,8 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, } *done = ((connssl->state == ssl_connection_complete) || (connssl->state == ssl_connection_deferred)); - CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", (int)result, + *done); return result; } @@ -2089,7 +2124,7 @@ static CURLcode gtls_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } @@ -2234,7 +2269,8 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zd", blen, nread); + CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> %d, %zd", blen, + (int)result, nread); return result; } @@ -2253,15 +2289,25 @@ static CURLcode gtls_random(struct Curl_easy *data, return rc ? CURLE_FAILED_INIT : CURLE_OK; } -static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode gtls_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum, /* output */ size_t sha256len) { struct sha256_ctx SHA256pw; sha256_init(&SHA256pw); - sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + sha256_update(&SHA256pw, ilen, input); + len -= ilen; + input += ilen; + } while(len); +#if NETTLE_VERSION_MAJOR >= 4 + (void)sha256len; + sha256_digest(&SHA256pw, sha256sum); +#else sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); +#endif return CURLE_OK; } diff --git a/deps/curl/lib/vtls/gtls.h b/deps/curl/lib/vtls/gtls.h index 49106dd869..4c662003eb 100644 --- a/deps/curl/lib/vtls/gtls.h +++ b/deps/curl/lib/vtls/gtls.h @@ -48,8 +48,6 @@ struct ssl_peer; struct ssl_connect_data; struct Curl_ssl_session; -int Curl_glts_get_ietf_proto(gnutls_session_t session); - struct gtls_shared_creds { gnutls_certificate_credentials_t creds; char *CAfile; /* CAfile path used to generate X509 store */ diff --git a/deps/curl/lib/vtls/keylog.c b/deps/curl/lib/vtls/keylog.c index 4ae2387a7a..094bf69db7 100644 --- a/deps/curl/lib/vtls/keylog.c +++ b/deps/curl/lib/vtls/keylog.c @@ -66,7 +66,7 @@ void Curl_tls_keylog_close(void) bool Curl_tls_keylog_enabled(void) { - return keylog_file_fp != NULL; + return !!keylog_file_fp; } const char *Curl_tls_keylog_file_name(void) @@ -102,9 +102,10 @@ bool Curl_tls_keylog_write_line(const char *line) return TRUE; } -bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen) +bool Curl_tls_keylog_write( + const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen) { size_t pos, i; unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + diff --git a/deps/curl/lib/vtls/keylog.h b/deps/curl/lib/vtls/keylog.h index 68ded4769d..b09fcc6f4d 100644 --- a/deps/curl/lib/vtls/keylog.h +++ b/deps/curl/lib/vtls/keylog.h @@ -61,9 +61,10 @@ const char *Curl_tls_keylog_file_name(void); * Appends a key log file entry. * Returns true iff the key log file is open and a valid entry was provided. */ -bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen); +bool Curl_tls_keylog_write( + const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF. diff --git a/deps/curl/lib/vtls/mbedtls.c b/deps/curl/lib/vtls/mbedtls.c index e2822668f0..57727613ae 100644 --- a/deps/curl/lib/vtls/mbedtls.c +++ b/deps/curl/lib/vtls/mbedtls.c @@ -141,7 +141,7 @@ static int mbedtls_bio_cf_write(void *bio, result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_WRITE; return result ? -1 : (int)nwritten; @@ -163,7 +163,7 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_READ; /* nread is never larger than int here */ @@ -179,10 +179,10 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) -static CURLcode -mbed_set_ssl_version_min_max(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - struct ssl_primary_config *conn_config) +static CURLcode mbed_set_ssl_version_min_max( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + struct ssl_primary_config *conn_config) { mbedtls_ssl_protocol_version ver_min = #ifdef MBEDTLS_SSL_PROTO_TLS1_2 @@ -275,15 +275,15 @@ static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) return id; } #else -#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str +#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str #define mbed_cipher_suite_walk_str Curl_cipher_suite_walk_str #endif -static CURLcode -mbed_set_selected_ciphers(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - const char *ciphers12, - const char *ciphers13) +static CURLcode mbed_set_selected_ciphers( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + const char *ciphers12, + const char *ciphers13) { const char *ciphers = ciphers12; const int *supported; @@ -307,7 +307,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, /* Add default TLSv1.3 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0) + if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; selected[count++] = id; @@ -360,7 +360,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, /* Add default TLSv1.2 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0) + if(!strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; /* No duplicates allowed (so selected cannot overflow) */ @@ -464,7 +464,8 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); failf(data, "mbedTLS: %s", buf); #else - failf(data, "mbedTLS: certificate verification error 0x%08x", *flags); + failf(data, "mbedTLS: certificate verification error 0x%08x", + (unsigned int)*flags); #endif } @@ -486,7 +487,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, const char * const ssl_capath = conn_config->CApath; #ifdef MBEDTLS_PEM_PARSE_C struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -529,7 +530,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error importing CA cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -541,7 +542,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert file %s: (-0x%04X) %s", - ssl_cafile, -ret, errorbuf); + ssl_cafile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } #else @@ -557,7 +558,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert path %s: (-0x%04X) %s", - ssl_capath, -ret, errorbuf); + ssl_capath, (unsigned int)-ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; @@ -581,7 +582,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; #ifdef MBEDTLS_PEM_PARSE_C - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -595,7 +596,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert file %s: (-0x%04X) %s", - ssl_cert, -ret, errorbuf); + ssl_cert, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } @@ -642,7 +643,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -662,12 +663,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_init(&backend->pk); - if(ssl_config->key || ssl_config->key_blob) { - if(ssl_config->key) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { + if(ssl_config->primary.key) { #ifdef MBEDTLS_FS_IO #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, - ssl_config->key_passwd); + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd); if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), @@ -677,8 +678,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else - ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, - ssl_config->key_passwd, + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd, mbedtls_ctr_drbg_random, &rng.drbg); if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || @@ -689,7 +690,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s", - ssl_config->key, -ret, errorbuf); + ssl_config->primary.key, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else @@ -698,12 +699,18 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, #endif } else { - const struct curl_blob *ssl_key_blob = ssl_config->key_blob; - const unsigned char *key_data = - (const unsigned char *)ssl_key_blob->data; - const char *passwd = ssl_config->key_passwd; + const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob; + const char *passwd = ssl_config->primary.key_passwd; + /* Unfortunately, mbedtls_pk_parse_key() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). */ + unsigned char *newblob = curlx_memdup0(ssl_key_blob->data, + ssl_key_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0); if(ret == 0 && @@ -715,7 +722,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, PSA_KEY_USAGE_SIGN_HASH))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #else - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0, mbedtls_ctr_drbg_random, @@ -724,11 +731,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #endif + curlx_free(newblob); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error parsing private key: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -738,7 +746,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, } static CURLcode mbed_load_crl(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = @@ -757,7 +765,7 @@ static CURLcode mbed_load_crl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CRL file %s: (-0x%04X) %s", - ssl_crlfile, -ret, errorbuf); + ssl_crlfile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } @@ -791,7 +799,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, char errorbuf[128]; infof(data, "mbedTLS: Connecting to %s:%d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.origin->hostname, connssl->peer.origin->port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -852,7 +860,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: ssl_setup failed: " - "(-0x%04X) %s", -ret, errorbuf); + "(-0x%04X) %s", (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -903,12 +911,12 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_load(&session, sc_session->sdata, sc_session->sdata_len); if(ret) { - failf(data, "SSL session error loading: -0x%x", -ret); + failf(data, "SSL session error loading: -0x%x", (unsigned int)-ret); } else { ret = mbedtls_ssl_set_session(&backend->ssl, &session); if(ret) - failf(data, "SSL session error setting: -0x%x", -ret); + failf(data, "SSL session error setting: -0x%x", (unsigned int)-ret); else infof(data, "SSL reusing session ID"); } @@ -926,13 +934,14 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, #endif ); - if(ssl_config->key || ssl_config->key_blob) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? - connssl->peer.sni : connssl->peer.hostname)) { + connssl->peer.sni : + connssl->peer.origin->hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and the name to set in the SNI extension. Thus even if curl connects to a host specified as an IP address, this function must be used. */ @@ -965,7 +974,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, result = (*data->set.ssl.fsslctx)(data, &backend->config, data->set.ssl.fsslctxp); if(result) - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); } return result; @@ -1046,7 +1055,7 @@ static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, mbedtls_ssl_get_version_number(&backend->ssl)); mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_handshake returned: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -1158,7 +1167,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_get_session(&backend->ssl, &session); msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED); if(ret) { - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); + failf(data, "mbedtls_ssl_get_session returned -0x%x", (unsigned int)-ret); result = CURLE_SSL_CONNECT_ERROR; goto out; } @@ -1177,7 +1186,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); if(ret) { - failf(data, "failed to serialize session: -0x%x", -ret); + failf(data, "failed to serialize session: -0x%x", (unsigned int)-ret); goto out; } @@ -1229,7 +1238,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", - len, -nwritten); + len, (unsigned int)-nwritten); switch(nwritten) { #ifdef MBEDTLS_SSL_PROTO_TLS1_3 case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1253,7 +1262,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d, %zu", - len, result, *pnwritten); + len, (int)result, *pnwritten); return result; } @@ -1297,7 +1306,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; default: - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; goto out; } @@ -1338,7 +1348,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; } else { - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; } @@ -1388,7 +1399,7 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else { char errorbuf[128]; CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", - buffersize, -nread); + buffersize, (unsigned int)-nread); switch(nread) { #ifdef MBEDTLS_SSL_SESSION_TICKETS case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1408,7 +1419,8 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; default: mbedtls_strerror(nread, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_read returned: (-0x%04X) %s", -nread, errorbuf); + failf(data, "ssl_read returned: (-0x%04X) %s", (unsigned int)-nread, + errorbuf); result = CURLE_RECV_ERROR; break; } @@ -1555,7 +1567,7 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { -#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 psa_status_t status; size_t sha256len_actual; status = psa_hash_compute(PSA_ALG_SHA_256, input, inputlen, diff --git a/deps/curl/lib/vtls/openssl.c b/deps/curl/lib/vtls/openssl.c index 4629ca4444..010bbb9825 100644 --- a/deps/curl/lib/vtls/openssl.c +++ b/deps/curl/lib/vtls/openssl.c @@ -33,7 +33,7 @@ #include "curl_trc.h" #include "httpsrr.h" #include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ +#include "url.h" /* for the SSL config check function */ #include "curlx/inet_pton.h" #include "vtls/openssl.h" #include "connect.h" @@ -127,9 +127,9 @@ #endif /* Whether SSL_CTX_set_ciphersuites is available. - * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no * LibreSSL: supported since 3.4.1 (released 2021-10-14) + * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) */ #if (!defined(LIBRESSL_VERSION_NUMBER) || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -142,9 +142,9 @@ #endif /* Whether SSL_CTX_set1_sigalgs_list is available - * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) * BoringSSL: supported since 0.20240913.0 (commit 826ce15) * LibreSSL: no + * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_SSL_CTX_SET1_SIGALGS @@ -152,10 +152,10 @@ #ifdef LIBRESSL_VERSION_NUMBER #define OSSL_PACKAGE "LibreSSL" -#elif defined(OPENSSL_IS_BORINGSSL) -#define OSSL_PACKAGE "BoringSSL" #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" +#elif defined(OPENSSL_IS_BORINGSSL) +#define OSSL_PACKAGE "BoringSSL" #elif defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \ !defined(OPENSSL_QUIC_API2) #define OSSL_PACKAGE "quictls" @@ -415,7 +415,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) if(result) break; - BIO_printf(mem, "%lx", X509_get_version(x)); + BIO_printf(mem, "%lx", (unsigned long)X509_get_version(x)); result = push_certinfo(data, mem, "Version", i); if(result) break; @@ -581,7 +581,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) (const uint8_t *)buf, (size_t)blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -610,7 +610,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &nread); CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -1188,13 +1188,13 @@ static int engineload(struct Curl_easy *data, /* Does the engine supports LOAD_CERT_CTRL ? */ if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, CURL_UNCONST(cmd_name), NULL)) { - failf(data, "ssl engine does not support loading certificates"); + failf(data, "SSL engine does not support loading certificates"); return 0; } /* Load the certificate from the engine */ if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, 0, ¶ms, NULL, 1)) { - failf(data, "ssl engine cannot load client cert with id '%s' [%s]", + failf(data, "SSL engine cannot load client cert with id '%s' [%s]", cert_file, ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); @@ -1202,7 +1202,7 @@ static int engineload(struct Curl_easy *data, } if(!params.cert) { - failf(data, "ssl engine did not initialized the certificate properly."); + failf(data, "SSL engine did not initialized the certificate properly."); return 0; } @@ -1671,7 +1671,7 @@ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name) char buf[256]; ENGINE_free(e); - failf(data, "Failed to initialise SSL Engine '%s': %s", + failf(data, "Failed to initialize SSL Engine '%s': %s", name, ossl_strerror(ERR_get_error(), buf, sizeof(buf))); result = CURLE_SSL_ENGINE_INITFAILED; e = NULL; @@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ - size_t hostlen = strlen(peer->hostname); + size_t hostlen = strlen(peer->origin->hostname); (void)conn; switch(peer->type) { case CURL_SSL_PEER_IPV4: - if(!curlx_inet_pton(AF_INET, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in_addr); break; #ifdef USE_IPV6 case CURL_SSL_PEER_IPV6: - if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET6, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in6_addr); @@ -2065,7 +2065,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, break; default: DEBUGASSERT(0); - failf(data, "unexpected ssl peer type: %d", peer->type); + failf(data, "unexpected SSL peer type: %d", (int)peer->type); return CURLE_PEER_FAILED_VERIFICATION; } @@ -2115,10 +2115,11 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == strlen(altptr)) && /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ - Curl_cert_hostcheck(altptr, altlen, peer->hostname, hostlen)) { + Curl_cert_hostcheck(altptr, altlen, + peer->origin->hostname, hostlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", - peer->dispname, (int)altlen, altptr); + peer->origin->user_hostname, (int)altlen, altptr); } break; @@ -2128,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's IP address!", - peer->dispname); + peer->origin->user_hostname); } break; } @@ -2144,9 +2145,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : (peer->type == CURL_SSL_PEER_IPV4) ? "ipv4 address" : "ipv6 address"; - infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); + infof(data, " subjectAltName does not match %s %s", tname, + peer->origin->user_hostname); failf(data, "SSL: no alternative certificate subject name matches " - "target %s '%s'", tname, peer->dispname); + "target %s '%s'", tname, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2190,7 +2192,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((cnlen <= 0) || !cn) result = CURLE_OUT_OF_MEMORY; else if((size_t)cnlen != strlen((char *)cn)) { - /* there was a terminating zero before the end of string, this + /* there was a null-terminator before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); result = CURLE_PEER_FAILED_VERIFICATION; @@ -2206,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)cn, cnlen, - peer->hostname, hostlen)) { + peer->origin->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target hostname '%s'", cn, peer->dispname); + "target hostname '%s'", cn, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2492,7 +2494,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, verstr = "TLSv1.3"; break; default: - curl_msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + curl_msnprintf(unknown, sizeof(unknown), "(%x)", (unsigned int)ssl_ver); verstr = unknown; break; } @@ -3010,7 +3012,7 @@ static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, result = load_cacert_from_memory(store, conn_config->ca_info_blob); if(result) { failf(data, "error adding trust anchors from certificate blob: %d", - result); + (int)result); return result; } infof(data, " CA Blob from configuration"); @@ -3322,13 +3324,13 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, return result; } -static CURLcode -ossl_init_session_and_alpns(struct ossl_ctx *octx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns_requested, - Curl_ossl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode ossl_init_session_and_alpns( + struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const struct alpn_spec *alpns_requested, + Curl_ossl_init_session_reuse_cb *sess_reuse_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf); @@ -3371,7 +3373,7 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, scs->alpn ? scs->alpn : "-"); octx->reused_session = TRUE; infof(data, "SSL verify result: %lx", - SSL_get_verify_result(octx->ssl)); + (unsigned long)SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA if(ssl_config->earlydata && scs->alpn && SSL_SESSION_get_max_early_data(ssl_session) && @@ -3428,9 +3430,9 @@ bool Curl_ossl_need_httpsrr(struct Curl_easy *data) { if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -3443,12 +3445,11 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, size_t ech_config_len = 0; char *outername = data->set.str[STRING_ECH_PUBLIC]; int trying_ech_now = 0; - CURLcode result = CURLE_OK; if(!CURLECH_ENABLED(data)) return CURLE_OK; - if(data->set.tls_ech & CURLECH_GREASE) { + if(data->set.tls_ech == CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); #ifdef HAVE_BORINGSSL_LIKE SSL_set_enable_ech_grease(octx->ssl, 1); @@ -3456,10 +3457,11 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); #endif } - else if(data->set.tls_ech & CURLECH_CLA_CFG) { + else if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { #ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; + CURLcode result; if(!b64) { infof(data, "ECH: ECHConfig from command line empty"); @@ -3469,12 +3471,12 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, result = curlx_base64_decode(b64, &ech_config, &ech_config_len); if(result || !ech_config) { infof(data, "ECH: cannot base64 decode ECHConfig from command line"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return result; } if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { curlx_free(ech_config); return CURLE_SSL_CONNECT_ERROR; } @@ -3490,7 +3492,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else @@ -3500,7 +3502,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -3509,7 +3511,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, infof(data, "ECH: ECHConfig from HTTPS RR"); if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { infof(data, "ECH: SSL_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -3519,7 +3521,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } } @@ -3531,13 +3533,14 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } #else if(trying_ech_now && outername) { + int ret; infof(data, "ECH: inner: '%s', outer: '%s'", - peer->hostname ? peer->hostname : "NULL", outername); - result = SSL_ech_set1_server_names(octx->ssl, - peer->hostname, outername, - 0 /* do send outer */); - if(result != 1) { - infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); + peer->origin->hostname ? peer->origin->hostname : "NULL", outername); + ret = SSL_ech_set1_server_names(octx->ssl, + peer->origin->hostname, outername, + 0 /* do send outer */); + if(ret != 1) { + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", ret); return CURLE_SSL_CONNECT_ERROR; } } @@ -3675,7 +3678,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; unsigned int ssl_version_min; char error_buffer[256]; @@ -3711,16 +3714,19 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; } - if(data->set.fdebug && data->set.verbose) { - /* the SSL trace callback is only used for verbose logging */ + if(data->set.fdebug && data->set.verbose && + (peer->transport != TRNSPRT_QUIC)) { + /* the SSL trace callback is only used for verbose logging; + * QUIC connections use a different TLS record format that + * ossl_trace cannot handle */ SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); } /* OpenSSL contains code to work around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those - work-arounds. The man page for this option states that SSL_OP_ALL enables - all the work-arounds and that "It is usually safe to use SSL_OP_ALL to + workarounds. The man page for this option states that SSL_OP_ALL enables + all the workarounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." @@ -3745,11 +3751,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, CVE-2010-4180 when using previous OpenSSL versions we no longer enable this option regardless of OpenSSL version and SSL_OP_ALL definition. - OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability: + OpenSSL added a workaround for an SSL 3.0/TLS 1.0 CBC vulnerability: https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt. - In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around + In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that workaround despite the fact that SSL_OP_ALL is documented to do "rather harmless" - workarounds. In order to keep the secure work-around, the + workarounds. In order to keep the secure workaround, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. */ @@ -3759,7 +3765,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ctx_options &= ~(ctx_option_t)SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; /* unless the user explicitly asks to allow the protocol vulnerability we - use the work-around */ + use the workaround */ if(!ssl_config->enable_beast) ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; @@ -3839,8 +3845,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(ssl_cert || ssl_cert_blob || ssl_cert_type) { result = client_cert(data, octx->ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); + ssl_config->primary.key, ssl_config->primary.key_blob, + ssl_config->primary.key_type, + ssl_config->primary.key_passwd); if(result) /* failf() is already done in client_cert() */ return result; @@ -3946,7 +3953,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, FALSE); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); return result; } } @@ -4009,6 +4016,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(octx); + DEBUGASSERT(connssl->peer.origin); result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, connssl->alpn, NULL, NULL, @@ -4217,7 +4225,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on - OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ + OpenSSL version above v1.1.1, not AWS-LC, BoringSSL, or LibreSSL */ else if((lib == ERR_LIB_SSL) && (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { /* If client certificate is required, communicate the @@ -4263,7 +4271,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, curlx_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->peer.hostname, connssl->peer.port); + connssl->peer.origin->hostname, connssl->peer.origin->port); } return result; @@ -4310,7 +4318,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config->verifypeer && !conn_config->verifyhost && - inner && !strcmp(inner, connssl->peer.hostname)) { + inner && !strcmp(inner, connssl->peer.origin->hostname)) { VERBOSE(status = "bad name (tolerated without peer verification)"); rv = SSL_ECH_STATUS_SUCCESS; } @@ -4333,7 +4341,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* trace retry_configs if we got some */ ossl_trace_ech_retry_configs(data, octx->ssl, 0); } - if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech & CURLECH_HARD)) { + if(rv != SSL_ECH_STATUS_SUCCESS && (data->set.tls_ech == CURLECH_HARD)) { infof(data, "ECH: ech-hard failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -4780,7 +4788,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, ossl_verify = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = ossl_verify; - infof(data, "OpenSSL verify result: %lx", ossl_verify); + infof(data, "OpenSSL verify result: %lx", (unsigned long)ossl_verify); verified = (ossl_verify == X509_V_OK); if(verified) @@ -4962,7 +4970,8 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, if(ssl_connect_1 == connssl->connecting_state) { if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } @@ -5240,7 +5249,7 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, connssl->input_pending = FALSE; } CURL_TRC_CF(data, cf, "ossl_recv(len=%zu) -> %d, %zu (in_pending=%d)", - buffersize, result, *pnread, connssl->input_pending); + buffersize, (int)result, *pnread, connssl->input_pending); return result; } @@ -5406,6 +5415,9 @@ size_t Curl_ossl_version(char *buffer, size_t size) *p = '_'; } return count; +#elif defined(OPENSSL_IS_AWSLC) + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); #elif defined(OPENSSL_IS_BORINGSSL) #ifdef CURL_BORINGSSL_VERSION return curl_msnprintf(buffer, size, "%s/%s", @@ -5413,9 +5425,6 @@ size_t Curl_ossl_version(char *buffer, size_t size) #else return curl_msnprintf(buffer, size, OSSL_PACKAGE); #endif -#elif defined(OPENSSL_IS_AWSLC) - return curl_msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); #else /* OpenSSL 3+ */ return curl_msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); @@ -5440,26 +5449,28 @@ static CURLcode ossl_random(struct Curl_easy *data, return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } -static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode ossl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { + CURLcode result = CURLE_OK; EVP_MD_CTX *mdctx; - unsigned int len = 0; (void)unused; mdctx = EVP_MD_CTX_create(); if(!mdctx) return CURLE_OUT_OF_MEMORY; if(!EVP_DigestInit(mdctx, EVP_sha256())) { - EVP_MD_CTX_destroy(mdctx); - return CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; + goto out; } - EVP_DigestUpdate(mdctx, tmp, tmplen); - EVP_DigestFinal_ex(mdctx, sha256sum, &len); + if(!EVP_DigestUpdate(mdctx, input, len) || + !EVP_DigestFinal_ex(mdctx, sha256sum, NULL)) + result = CURLE_BAD_FUNCTION_ARGUMENT; +out: EVP_MD_CTX_destroy(mdctx); - return CURLE_OK; + return result; } static bool ossl_cert_status_request(void) diff --git a/deps/curl/lib/vtls/openssl.h b/deps/curl/lib/vtls/openssl.h index 61d4a1757e..4fa466b367 100644 --- a/deps/curl/lib/vtls/openssl.h +++ b/deps/curl/lib/vtls/openssl.h @@ -33,7 +33,7 @@ * , , or something else, does this: * #define X509_NAME ((LPCSTR)7) * - * In BoringSSL/AWC-LC's there is: + * In AWC-LC/BoringSSL's there is: * typedef struct X509_name_st X509_NAME; * etc. * @@ -74,7 +74,7 @@ #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ #endif -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #define HAVE_BORINGSSL_LIKE #endif @@ -86,9 +86,9 @@ /* * Whether SSL_CTX_set_keylog_callback is available. - * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. + * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_KEYLOG_CALLBACK @@ -107,12 +107,12 @@ struct Curl_ssl_session; /* Struct to hold a curl OpenSSL instance */ struct ossl_ctx { /* these ones requires specific SSL-types */ - SSL_CTX* ssl_ctx; - SSL* ssl; + SSL_CTX *ssl_ctx; + SSL *ssl; BIO_METHOD *bio_method; CURLcode io_result; /* result of last BIO cfilter operation */ /* blocked writes need to retry with same length, remember it */ - int blocked_ssl_write_len; + int blocked_ssl_write_len; #if !defined(HAVE_KEYLOG_UPSTREAM) && !defined(HAVE_KEYLOG_CALLBACK) /* Set to true once a valid keylog entry has been created to avoid dupes. This is a bool and not a bitfield because it is passed by address. */ @@ -183,7 +183,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, /* * Get the server cert, verify it and show it, etc., only call failf() if - * ssl config verifypeer or -host is set. Otherwise all this is for + * SSL config verifypeer or -host is set. Otherwise all this is for * informational purposes only! */ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, diff --git a/deps/curl/lib/vtls/rustls.c b/deps/curl/lib/vtls/rustls.c index d886c85052..950f170212 100644 --- a/deps/curl/lib/vtls/rustls.c +++ b/deps/curl/lib/vtls/rustls.c @@ -45,6 +45,12 @@ #include "curlx/base64.h" #endif +#if EAGAIN != EWOULDBLOCK +#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK || (e) == EAGAIN) +#else +#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK) +#endif + struct rustls_ssl_backend_data { const struct rustls_client_config *config; struct rustls_connection *conn; @@ -118,7 +124,7 @@ static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, connssl->peer_closed = TRUE; *out_n = (uintptr_t)nread; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %d, %zu", - (size_t)len, result, nread); + (size_t)len, (int)result, nread); return ret; } @@ -141,7 +147,7 @@ static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, } *out_n = (uintptr_t)nwritten; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %d, %zu", - len, result, nwritten); + len, (int)result, nwritten); return ret; } @@ -160,7 +166,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, io_ctx.data = data; io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx, &tls_bytes_read); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + if(RAW_EAGAIN(io_error)) { *err = CURLE_AGAIN; return -1; } @@ -252,7 +258,7 @@ static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: CURL_TRC_CF(data, cf, "rustls_recv(len=%zu) -> %d, %zu", - plainlen, result, *pnread); + plainlen, (int)result, *pnread); return result; } @@ -270,7 +276,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, while(rustls_connection_wants_write(rconn)) { io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, &tlswritten); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + if(RAW_EAGAIN(io_error)) { CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", tlswritten_total); return CURLE_AGAIN; @@ -328,7 +334,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(backend->plain_out_buffered) { result = cr_flush_out(cf, data, rconn); CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", - backend->plain_out_buffered, result); + backend->plain_out_buffered, (int)result); if(result) return result; if(blen > backend->plain_out_buffered) { @@ -337,7 +343,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else blen = 0; - *pnwritten += (ssize_t)backend->plain_out_buffered; + *pnwritten += backend->plain_out_buffered; backend->plain_out_buffered = 0; } @@ -370,11 +376,11 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } else - *pnwritten += (ssize_t)plainwritten; + *pnwritten += plainwritten; out: CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", - plainlen, result, *pnwritten); + plainlen, (int)result, *pnwritten); return result; } @@ -845,14 +851,14 @@ init_config_builder_client_auth(struct Curl_easy *data, const struct rustls_certified_key *certified_key = NULL; CURLcode result = CURLE_OK; - if(conn_config->clientcert && !ssl_config->key) { + if(conn_config->clientcert && !ssl_config->primary.key) { failf(data, "rustls: must provide key with certificate '%s'", conn_config->clientcert); return CURLE_SSL_CERTPROBLEM; } - else if(!conn_config->clientcert && ssl_config->key) { + else if(!conn_config->clientcert && ssl_config->primary.key) { failf(data, "rustls: must provide certificate with key '%s'", - ssl_config->key); + ssl_config->primary.key); return CURLE_SSL_CERTPROBLEM; } @@ -866,8 +872,9 @@ init_config_builder_client_auth(struct Curl_easy *data, goto cleanup; } - if(!read_file_into(ssl_config->key, &key_contents)) { - failf(data, "rustls: failed to read key file: '%s'", ssl_config->key); + if(!read_file_into(ssl_config->primary.key, &key_contents)) { + failf(data, "rustls: failed to read key file: '%s'", + ssl_config->primary.key); result = CURLE_SSL_CERTPROBLEM; goto cleanup; } @@ -915,9 +922,9 @@ static bool cr_ech_need_httpsrr(struct Curl_easy *data) { if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -957,7 +964,7 @@ init_config_builder_ech(struct Curl_easy *data, return CURLE_OK; } - if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { const char *b64 = data->set.str[STRING_ECH_CONFIG]; size_t decode_result; if(!b64) { @@ -974,8 +981,9 @@ init_config_builder_ech(struct Curl_easy *data, } } else { + const struct ssl_connect_data *connssl = cf->ctx; const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.origin); if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); @@ -997,7 +1005,7 @@ init_config_builder_ech(struct Curl_easy *data, } cleanup: /* if we base64 decoded, we can free now */ - if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { curlx_free(ech_config); } if(dns) { @@ -1042,6 +1050,12 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, config_builder, cr_verify_none); } else if(ssl_config->native_ca_store) { + if(conn_config->CRLfile) { + failf(data, "rustls: CRL file not supported with native CA store; " + "the platform verifier has no CRL attachment API"); + rustls_client_config_builder_free(config_builder); + return CURLE_NOT_BUILT_IN; + } result = init_config_builder_platform_verifier(data, config_builder); if(result != CURLE_OK) { rustls_client_config_builder_free(config_builder); @@ -1060,7 +1074,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, } } - if(conn_config->clientcert || ssl_config->key) { + if(conn_config->clientcert || ssl_config->primary.key) { result = init_config_builder_client_auth(data, conn_config, ssl_config, @@ -1074,7 +1088,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, #ifdef USE_ECH if(CURLECH_ENABLED(data)) { result = init_config_builder_ech(data, cf, config_builder); - if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { + if((result != CURLE_OK) && (data->set.tls_ech == CURLECH_HARD)) { rustls_client_config_builder_free(config_builder); return result; } @@ -1093,9 +1107,9 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - DEBUGASSERT(rconn == NULL); + DEBUGASSERT(!rconn); rr = rustls_client_connection_new(backend->config, - connssl->peer.hostname, + connssl->peer.origin->hostname, &rconn); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "rustls_client_connection_new"); @@ -1142,14 +1156,15 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); + CURL_TRC_CF(data, cf, "cr_connect, state=%d", (int)connssl->state); *done = FALSE; #ifdef USE_ECH /* if we do ECH and need the HTTPS-RR information for it, * we delay the connect until it arrives or DNS resolve fails. */ if(cr_ech_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } @@ -1159,7 +1174,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, result = cr_init_backend(cf, data, (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", (int)result); if(result) return result; connssl->state = ssl_connection_negotiating; @@ -1351,7 +1366,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown send failed: %d", result); + CURL_TRC_CF(data, cf, "shutdown send failed: %d", (int)result); goto out; } @@ -1368,7 +1383,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(result) { DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)result); } else if(nread == 0) { /* We got the close notify alert and are done. */ diff --git a/deps/curl/lib/vtls/schannel.c b/deps/curl/lib/vtls/schannel.c index fb5ff0e9d8..3e871227aa 100644 --- a/deps/curl/lib/vtls/schannel.c +++ b/deps/curl/lib/vtls/schannel.c @@ -69,7 +69,7 @@ #define SCH_DEV_SHOWBOOL(x) do {} while(0) #endif -/* Offered by mingw-w64 v8+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 7.0A/VS2010+ */ #ifndef SP_PROT_TLS1_0_CLIENT #define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT #endif @@ -80,15 +80,16 @@ #define SP_PROT_TLS1_2_CLIENT 0x00000800 #endif -/* Offered by mingw-w64 v8+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef SP_PROT_TLS1_3_CLIENT #define SP_PROT_TLS1_3_CLIENT 0x00002000 #endif +/* Offered by mingw-w64 v8+, MS SDK 8.1/~VS2013+ */ #ifndef SCH_USE_STRONG_CRYPTO #define SCH_USE_STRONG_CRYPTO 0x00400000 #endif -/* Offered by mingw-w64 v10+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v10+, MS SDK 7.0A/VS2010+ */ #ifndef SECBUFFER_ALERT #define SECBUFFER_ALERT 17 #endif @@ -105,12 +106,12 @@ * #define failf(x, y, ...) curl_mprintf(y, __VA_ARGS__) */ -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 #endif -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_FIND_HAS_PRIVATE_KEY #define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT) #endif @@ -166,7 +167,7 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, VERSION_GREATER_THAN_EQUAL)) { ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; } - else /* Windows 10 and older */ + else /* Windows 10 or older */ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; @@ -191,8 +192,8 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; break; } - else { /* Windows 10 and older */ - failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11"); + else { /* Windows 10 or older */ + failf(data, "schannel: TLS 1.3 not supported on Windows 10 or older"); return CURLE_SSL_CONNECT_ERROR; } } @@ -252,12 +253,12 @@ static const struct algo algs[] = { CIPHEROPTION(CALG_SHA_384), CIPHEROPTION(CALG_SHA_512), CIPHEROPTION(CALG_ECDH), -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifdef CALG_ECMQV CIPHEROPTION(CALG_ECMQV), #endif CIPHEROPTION(CALG_ECDSA), -/* Offered by mingw-w64 v7+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v7+, MS SDK 7.0A/VS2010+ */ #ifdef CALG_ECDH_EPHEM CIPHEROPTION(CALG_ECDH_EPHEM), #endif @@ -359,15 +360,17 @@ static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, return CURLE_OK; } -static CURLcode get_client_cert(struct Curl_easy *data, +static CURLcode get_client_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, HCERTSTORE *out_cert_store, PCCERT_CONTEXT *out_cert_context) { + struct ssl_primary_config *sslc = Curl_ssl_cf_get_primary_config(cf); PCCERT_CONTEXT client_cert = NULL; HCERTSTORE client_cert_store = NULL; CURLcode result = CURLE_OK; - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { + if(sslc->clientcert || sslc->cert_blob) { DWORD cert_store_name = 0; TCHAR *cert_store_path = NULL; TCHAR *cert_thumbprint_str = NULL; @@ -378,15 +381,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, FILE *fInCert = NULL; void *certdata = NULL; size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; + bool blob = !!sslc->cert_blob; if(blob) { - certdata = data->set.ssl.primary.cert_blob->data; - certsize = data->set.ssl.primary.cert_blob->len; + certdata = sslc->cert_blob->data; + certsize = sslc->cert_blob->len; } else { TCHAR *cert_path = - curlx_convert_UTF8_to_tchar(data->set.ssl.primary.clientcert); + curlx_convert_UTF8_to_tchar(sslc->clientcert); if(!cert_path) return CURLE_OUT_OF_MEMORY; @@ -403,22 +406,22 @@ static CURLcode get_client_cert(struct Curl_easy *data, } curlx_free(cert_path); - if(result && (data->set.ssl.primary.clientcert[0] != '\0')) - fInCert = curlx_fopen(data->set.ssl.primary.clientcert, "rb"); + if(result && (sslc->clientcert[0] != '\0')) + fInCert = curlx_fopen(sslc->clientcert, "rb"); if(result && !fInCert) { failf(data, "schannel: Failed to get certificate location" " or file for %s", - data->set.ssl.primary.clientcert); + sslc->clientcert); return result; } } - if((fInCert || blob) && data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "P12")) { + if((fInCert || blob) && sslc->cert_type && + !curl_strequal(sslc->cert_type, "P12")) { failf(data, "schannel: certificate format compatibility error " "for %s", - blob ? "(memory blob)" : data->set.ssl.primary.clientcert); + blob ? "(memory blob)" : sslc->clientcert); curlx_free(cert_store_path); if(fInCert) curlx_fclose(fInCert); @@ -434,7 +437,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, size_t pwd_len = 0; int cert_find_flags; const char *cert_showfilename_error = blob ? - "(memory blob)" : data->set.ssl.primary.clientcert; + "(memory blob)" : sslc->clientcert; curlx_free(cert_store_path); if(fInCert) { long cert_tell = 0; @@ -455,7 +458,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, curlx_fclose(fInCert); if(!continue_reading) { failf(data, "schannel: Failed to read cert file %s", - data->set.ssl.primary.clientcert); + sslc->clientcert); curlx_free(certdata); return CURLE_SSL_CERTPROBLEM; } @@ -465,15 +468,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, datablob.pbData = (BYTE *)certdata; datablob.cbData = (DWORD)certsize; - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); + if(sslc->key_passwd) + pwd_len = strlen(sslc->key_passwd); + pszPassword = curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); if(pszPassword) { int str_w_len = 0; if(pwd_len > 0) str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - data->set.ssl.key_passwd, + sslc->key_passwd, (int)pwd_len, pszPassword, (int)(pwd_len + 1)); @@ -653,6 +656,11 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, if(ciphers) { if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + if(!(enabled_protocols & ~SP_PROT_TLS1_3_CLIENT)) { + failf(data, "schannel: TLS 1.3 is not supported with a cipher list; " + "remove the cipher list or allow a lower TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } infof(data, "schannel: WARNING: This version of Schannel " "negotiates a less-secure TLS version than TLS 1.3 because the " "user set an algorithm cipher list."); @@ -789,7 +797,7 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = get_client_cert(data, &client_cert_store, &client_cert); + result = get_client_cert(cf, data, &client_cert_store, &client_cert); if(result) return result; @@ -843,7 +851,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; @@ -895,7 +903,8 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; + snihost = connssl->peer.sni ? + connssl->peer.sni : connssl->peer.origin->hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; @@ -979,7 +988,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, } /* Schannel InitializeSecurityContext: - https://learn.microsoft.com/windows/win32/api/rrascfg/nn-rrascfg-ieapproviderconfig + https://learn.microsoft.com/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw At the moment we do not pass inbuf unless we are using ALPN since we only use it for that, and WINE (for which we currently disable ALPN) is giving @@ -1238,7 +1247,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1484,9 +1493,9 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, static bool valid_cert_encoding(const CERT_CONTEXT *cert_context) { - return (cert_context != NULL) && + return cert_context && ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (cert_context->pbCertEncoded != NULL) && + cert_context->pbCertEncoded && (cert_context->cbCertEncoded > 0); } @@ -1579,6 +1588,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = CURLE_OK; SECURITY_STATUS sspi_status = SEC_E_OK; CERT_CONTEXT *ccert_context = NULL; @@ -1590,7 +1600,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -1656,7 +1666,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, return result; } - if(data->set.ssl.certinfo) { + if(ssl_config->certinfo) { int certs_count = 0; sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, @@ -1732,7 +1742,7 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+. MS SDK 7.0A+. */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ /* When SSPI is used in combination with Schannel * we need the Schannel context to create the Schannel * binding to pass the IIS extended protection checks. @@ -1994,7 +2004,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* calculate the complete message length and allocate a buffer for it */ data_len = backend->stream_sizes.cbHeader + len + backend->stream_sizes.cbTrailer; - ptr = (unsigned char *)curlx_malloc(data_len); + ptr = curlx_malloc(data_len); if(!ptr) { return CURLE_OUT_OF_MEMORY; } @@ -2183,7 +2193,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); else { - infof(data, "schannel: recv returned error %d", result); + infof(data, "schannel: recv returned error %d", (int)result); backend->recv_unrecoverable_err = result; } } @@ -2428,7 +2438,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, *done = FALSE; if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.origin->hostname, connssl->peer.origin->port); } if(!backend->ctxt || cf->shutdown) { @@ -2502,7 +2512,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, else { if(!backend->recv_connection_closed) { result = CURLE_SEND_ERROR; - failf(data, "schannel: error sending close msg: %d", result); + failf(data, "schannel: error sending close msg: %d", (int)result); goto out; } /* Looks like server already closed the connection. @@ -2525,7 +2535,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_RECV; } else if(result) { - CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", (int)result); result = CURLE_RECV_ERROR; } else if(nread == 0) { @@ -2633,16 +2643,24 @@ static CURLcode schannel_random(struct Curl_easy *data, return Curl_win32_random(entropy, length); } -static void schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) +static CURLcode schannel_checksum(const unsigned char *input, + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) { + CURLcode result = CURLE_FAILED_INIT; + HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; + size_t off; + + DWORD cbHashSize; + DWORD dwHashSizeLen; + DWORD dwChecksumLen; + /* since this can fail in multiple ways, zero memory first so we never * return old data */ @@ -2650,37 +2668,45 @@ static void schannel_checksum(const unsigned char *input, if(!CryptAcquireContext(&hProv, NULL, NULL, provType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return; /* failed */ + goto out; - do { - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) + goto out; - if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) - break; /* failed */ + result = CURLE_BAD_FUNCTION_ARGUMENT; - if(!CryptHashData(hHash, input, (DWORD)inputlen, 0)) - break; /* failed */ + off = 0; + while(off < inputlen) { + DWORD chunk = (DWORD)CURLMIN(inputlen - off, 0xffffffffUL); + if(!CryptHashData(hHash, input + off, chunk, 0)) + goto out; + off += chunk; + } - /* get hash size */ - if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, - &dwHashSizeLen, 0)) - break; /* failed */ + /* get hash size */ + cbHashSize = 0; + dwHashSizeLen = (DWORD)sizeof(cbHashSize); + if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, + &dwHashSizeLen, 0)) + goto out; - /* check hash size */ - if(checksumlen < cbHashSize) - break; /* failed */ + /* check if hash fits into the return buffer */ + if(checksumlen < cbHashSize) + goto out; - if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) - break; /* failed */ - } while(0); + dwChecksumLen = (DWORD)checksumlen; + if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0) && + dwChecksumLen == cbHashSize) + result = CURLE_OK; +out: if(hHash) CryptDestroyHash(hHash); if(hProv) CryptReleaseContext(hProv, 0); + + return result; } static CURLcode schannel_sha256sum(const unsigned char *input, @@ -2688,9 +2714,8 @@ static CURLcode schannel_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { - schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; + return schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); } static void *schannel_get_internals(struct ssl_connect_data *connssl, @@ -2748,10 +2773,11 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, if(share->CAinfo_blob_size != ca_info_blob->len) { return NULL; } - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - info_blob_digest, - CURL_SHA256_DIGEST_LENGTH); + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) + return NULL; if(memcmp(share->CAinfo_blob_digest, info_blob_digest, CURL_SHA256_DIGEST_LENGTH)) { return NULL; @@ -2789,7 +2815,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct schannel_cert_share *share; - size_t CAinfo_blob_size = 0; + unsigned char digest[CURL_SHA256_DIGEST_LENGTH]; char *CAfile = NULL; DEBUGASSERT(multi); @@ -2798,12 +2824,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, return FALSE; } + if(ca_info_blob) { + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, digest, sizeof(digest))) { + return FALSE; + } + } + else if(conn_config->CAfile) { + CAfile = curlx_strdup(conn_config->CAfile); + if(!CAfile) { + return FALSE; + } + } + share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); if(!share) { share = curlx_calloc(1, sizeof(*share)); if(!share) { + curlx_free(CAfile); return FALSE; } if(!Curl_hash_add2(&multi->proto_hash, @@ -2811,35 +2851,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1, share, schannel_cert_share_free)) { curlx_free(share); + curlx_free(CAfile); return FALSE; } } - if(ca_info_blob) { - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - share->CAinfo_blob_digest, - CURL_SHA256_DIGEST_LENGTH); - CAinfo_blob_size = ca_info_blob->len; - } - else { - if(conn_config->CAfile) { - CAfile = curlx_strdup(conn_config->CAfile); - if(!CAfile) { - return FALSE; - } - } - } - /* free old cache data */ if(share->cert_store) { CertCloseStore(share->cert_store, 0); } curlx_free(share->CAfile); + if(ca_info_blob) { + memcpy(share->CAinfo_blob_digest, digest, sizeof(digest)); + share->CAinfo_blob_size = ca_info_blob->len; + } + else + share->CAinfo_blob_size = 0; + share->time = curlx_now(); share->cert_store = cert_store; - share->CAinfo_blob_size = CAinfo_blob_size; share->CAfile = CAfile; return TRUE; } diff --git a/deps/curl/lib/vtls/schannel_int.h b/deps/curl/lib/vtls/schannel_int.h index 496635f2c2..b65ff79926 100644 --- a/deps/curl/lib/vtls/schannel_int.h +++ b/deps/curl/lib/vtls/schannel_int.h @@ -42,7 +42,7 @@ #define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)(size_t)10) #endif -/* Offered by mingw-w64 v8+, MS SDK ~10+/~VS2022+ */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.17763.0/VS2017 15.8+ */ #ifndef SCH_CREDENTIALS_VERSION #define SCH_CREDENTIALS_VERSION 0x00000005 @@ -56,42 +56,42 @@ typedef enum _eTlsAlgorithmUsage { /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _CRYPTO_SETTINGS { - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ - DWORD dwMinBitLength; - DWORD dwMaxBitLength; -} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, *PCRYPTO_SETTINGS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _TLS_PARAMETERS { - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; -} TLS_PARAMETERS, * PTLS_PARAMETERS; + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, *PTLS_PARAMETERS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _SCH_CREDENTIALS { - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT *paCred; + HCERTSTORE hRootStore; - DWORD cMappers; + DWORD cMappers; struct _HMAPPER **aphMappers; - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; -} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, *PSCH_CREDENTIALS; -#endif /* SCH_CREDENTIALS_VERSION */ +#endif /* !SCH_CREDENTIALS_VERSION */ struct Curl_schannel_cred { CredHandle cred_handle; diff --git a/deps/curl/lib/vtls/schannel_verify.c b/deps/curl/lib/vtls/schannel_verify.c index 9be6fe311c..e5fe2249a6 100644 --- a/deps/curl/lib/vtls/schannel_verify.c +++ b/deps/curl/lib/vtls/schannel_verify.c @@ -71,7 +71,7 @@ struct cert_chain_engine_config_win8 { DWORD dwExclusiveFlags; }; -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG #define CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG 0x00000001 #endif @@ -92,11 +92,6 @@ struct cert_chain_engine_config_win7 { HCERTSTORE hExclusiveTrustedPeople; }; -static int is_cr_or_lf(char c) -{ - return c == '\r' || c == '\n'; -} - /* Search the substring needle,needlelen into string haystack,haystacklen * Strings do not need to be terminated by a '\0'. * Similar of macOS/Linux memmem (not available on Visual Studio). @@ -112,7 +107,7 @@ static const char *c_memmem(const void *haystack, size_t haystacklen, return NULL; first = *(const char *)needle; for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) + if((*p == first) && !memcmp(p, needle, needlelen)) return p; return NULL; @@ -134,10 +129,11 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, while(more_certs && (current_ca_file_ptr < ca_buffer_limit)) { const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, - ca_buffer_limit-current_ca_file_ptr, + ca_buffer_limit - + current_ca_file_ptr - 1, BEGIN_CERT, begin_cert_len); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { + if(!begin_cert_ptr || !ISNEWLINE(begin_cert_ptr[begin_cert_len])) { more_certs = 0; } else { @@ -156,7 +152,6 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, CERT_BLOB cert_blob; const CERT_CONTEXT *cert_context = NULL; BOOL add_cert_result = FALSE; - DWORD actual_content_type = 0; DWORD cert_size = (DWORD)((end_cert_ptr + end_cert_len) - begin_cert_ptr); @@ -166,10 +161,10 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &cert_blob, CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, + CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED, 0, NULL, - &actual_content_type, + NULL, NULL, NULL, NULL, @@ -186,51 +181,27 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, else { current_ca_file_ptr = begin_cert_ptr + cert_size; - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { + add_cert_result = + CertAddCertificateContextToStore(trust_store, + cert_context, + CERT_STORE_ADD_ALWAYS, + NULL); + if(!add_cert_result) { + char buffer[WINAPI_ERROR_LEN]; failf(data, - "schannel: unexpected content type '%lu' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file_text); + "schannel: failed to add certificate from CA file '%s' " + "to certificate store: %s", + ca_file_text, + curlx_winapi_strerror(GetLastError(), buffer, + sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; more_certs = 0; } else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - if(!add_cert_result) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file_text, - curlx_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } + num_certs++; } - switch(actual_content_type) { - case CERT_QUERY_CONTENT_CERT: - case CERT_QUERY_CONTENT_SERIALIZED_CERT: - CertFreeCertificateContext(cert_context); - break; - case CERT_QUERY_CONTENT_CRL: - case CERT_QUERY_CONTENT_SERIALIZED_CRL: - CertFreeCRLContext((PCCRL_CONTEXT)cert_context); - break; - case CERT_QUERY_CONTENT_CTL: - case CERT_QUERY_CONTENT_SERIALIZED_CTL: - CertFreeCTLContext((PCCTL_CONTEXT)cert_context); - break; - } + CertFreeCertificateContext(cert_context); } } } @@ -254,73 +225,73 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, struct Curl_easy *data) { CURLcode result; - HANDLE ca_file_handle; - LARGE_INTEGER file_size; + FILE *ca_file_handle; char *ca_file_buffer = NULL; - size_t ca_file_bufsize = 0; - DWORD total_bytes_read = 0; + long ca_file_bufsize = 0; + long total_bytes_read = 0; /* * Read the CA file completely into memory before parsing it. This * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ - ca_file_handle = curlx_CreateFile(ca_file, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(ca_file_handle == INVALID_HANDLE_VALUE) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to open CA file '%s': %s", ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file_handle = curlx_fopen(ca_file, "rb"); + if(!ca_file_handle) { + failf(data, "schannel: failed to open CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(!GetFileSizeEx(ca_file_handle, &file_size)) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to determine size of CA file '%s': %s", - ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(curlx_fseek(ca_file_handle, 0, SEEK_END)) { + failf(data, "schannel: failed seeking to end of CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(file_size.QuadPart > MAX_CAFILE_SIZE) { + ca_file_bufsize = ftell(ca_file_handle); + + if(curlx_fseek(ca_file_handle, 0, SEEK_SET)) { + failf(data, "schannel: failed seeking to beginning of CA file '%s'", + ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(ca_file_bufsize < 0) { + failf(data, "schannel: failed to get length of CA file '%s'", ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(ca_file_bufsize > MAX_CAFILE_SIZE) { failf(data, "schannel: CA file exceeds max size of %d bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - ca_file_bufsize = (size_t)file_size.QuadPart; - ca_file_buffer = (char *)curlx_malloc(ca_file_bufsize + 1); + ca_file_buffer = curlx_malloc(ca_file_bufsize + 1); if(!ca_file_buffer) { result = CURLE_OUT_OF_MEMORY; goto cleanup; } while(total_bytes_read < ca_file_bufsize) { - DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); - DWORD bytes_read = 0; + size_t nread = fread(ca_file_buffer + total_bytes_read, 1, + ca_file_bufsize - total_bytes_read, ca_file_handle); - if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, - bytes_to_read, &bytes_read, NULL)) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to read from CA file '%s': %s", ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(ferror(ca_file_handle)) { + failf(data, "schannel: failed to read from CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(bytes_read == 0) { + + if(nread == 0) { /* Premature EOF -- adjust the bufsize to the new value */ ca_file_bufsize = total_bytes_read; } else { - total_bytes_read += bytes_read; + total_bytes_read += (long)nread; } } @@ -333,8 +304,8 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, data); cleanup: - if(ca_file_handle != INVALID_HANDLE_VALUE) { - CloseHandle(ca_file_handle); + if(ca_file_handle) { + curlx_fclose(ca_file_handle); } curlx_safefree(ca_file_buffer); @@ -362,7 +333,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(Win8_compat) { -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 #endif @@ -382,7 +353,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, if(!alt_name_info) return 0; - compute_content = host_names != NULL && length != 0; + compute_content = host_names && length != 0; /* Initialize default return values. */ actual_length = 1; @@ -509,7 +480,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) SECURITY_STATUS sspi_status; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->peer.hostname; + const char *conn_hostname = connssl->peer.origin->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -572,7 +543,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ - cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); + cert_hostname_buff = curlx_malloc(len * sizeof(TCHAR)); if(!cert_hostname_buff) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -724,15 +695,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(ca_info_blob) { result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); } else { result = add_certs_file_to_store(trust_store, - conn_config->CAfile, - data); + conn_config->CAfile, + data); } if(result == CURLE_OK) { if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { @@ -780,9 +751,13 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(result == CURLE_OK) { CERT_CHAIN_PARA ChainPara; + LPSTR serverAuthOID = CURL_UNCONST(szOID_PKIX_KP_SERVER_AUTH); memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &serverAuthOID; if(!CertGetCertificateChain(cert_chain_engine, pCertContextServer, @@ -805,7 +780,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - if(data->set.ssl.revoke_best_effort) { + if(ssl_config->revoke_best_effort) { /* Ignore errors when root certificates are missing the revocation * list URL, or when the list could not be downloaded because the * server is currently unreachable. */ diff --git a/deps/curl/lib/vtls/vtls.c b/deps/curl/lib/vtls/vtls.c index 891facfabb..82ce007b3d 100644 --- a/deps/curl/lib/vtls/vtls.c +++ b/deps/curl/lib/vtls/vtls.c @@ -78,57 +78,6 @@ #include #endif - -#define CLONE_STRING(var) \ - do { \ - if(source->var) { \ - dest->var = curlx_strdup(source->var); \ - if(!dest->var) \ - return FALSE; \ - } \ - else \ - dest->var = NULL; \ - } while(0) - -#define CLONE_BLOB(var) \ - do { \ - if(blobdup(&dest->var, source->var)) \ - return FALSE; \ - } while(0) - -static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src) -{ - DEBUGASSERT(dest); - DEBUGASSERT(!*dest); - if(src) { - /* only if there is data to dupe! */ - struct curl_blob *d; - d = curlx_malloc(sizeof(struct curl_blob) + src->len); - if(!d) - return CURLE_OUT_OF_MEMORY; - d->len = src->len; - /* Always duplicate because the connection may survive longer than the - handle that passed in the blob. */ - d->flags = CURL_BLOB_COPY; - d->data = (void *)((char *)d + sizeof(struct curl_blob)); - memcpy(d->data, src->data, src->len); - *dest = d; - } - return CURLE_OK; -} - -/* returns TRUE if the blobs are identical */ -static bool blobcmp(struct curl_blob *first, struct curl_blob *second) -{ - if(!first && !second) /* both are NULL */ - return TRUE; - if(!first || !second) /* one is NULL */ - return FALSE; - if(first->len != second->len) /* different sizes */ - return FALSE; - return !memcmp(first->data, second->data, first->len); /* same data */ -} - #ifdef USE_SSL #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static const struct alpn_spec ALPN_SPEC_H11 = { @@ -177,265 +126,6 @@ static const struct alpn_spec *alpn_get_spec(http_majors wanted, #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ #endif /* USE_SSL */ -void Curl_ssl_easy_config_init(struct Curl_easy *data) -{ - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ - data->set.ssl.primary.verifypeer = TRUE; - data->set.ssl.primary.verifyhost = TRUE; - data->set.ssl.primary.cache_session = TRUE; /* caching by default */ -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl = data->set.ssl; -#endif -} - -static bool match_ssl_primary_config(struct Curl_easy *data, - struct ssl_primary_config *c1, - struct ssl_primary_config *c2) -{ - (void)data; - if((c1->version == c2->version) && - (c1->version_max == c2->version_max) && - (c1->ssl_options == c2->ssl_options) && - (c1->verifypeer == c2->verifypeer) && - (c1->verifyhost == c2->verifyhost) && - (c1->verifystatus == c2->verifystatus) && - blobcmp(c1->cert_blob, c2->cert_blob) && - blobcmp(c1->ca_info_blob, c2->ca_info_blob) && - blobcmp(c1->issuercert_blob, c2->issuercert_blob) && - Curl_safecmp(c1->CApath, c2->CApath) && - Curl_safecmp(c1->CAfile, c2->CAfile) && - Curl_safecmp(c1->issuercert, c2->issuercert) && - Curl_safecmp(c1->clientcert, c2->clientcert) && -#ifdef USE_TLS_SRP - !Curl_timestrcmp(c1->username, c2->username) && - !Curl_timestrcmp(c1->password, c2->password) && -#endif - curl_strequal(c1->cipher_list, c2->cipher_list) && - curl_strequal(c1->cipher_list13, c2->cipher_list13) && - curl_strequal(c1->curves, c2->curves) && - curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && - curl_strequal(c1->CRLfile, c2->CRLfile) && - curl_strequal(c1->pinned_key, c2->pinned_key)) - return TRUE; - - return FALSE; -} - -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy) -{ -#ifndef CURL_DISABLE_PROXY - if(proxy) - return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, - &candidate->proxy_ssl_config); -#else - (void)proxy; -#endif - return match_ssl_primary_config(data, &data->set.ssl.primary, - &candidate->ssl_config); -} - -static bool clone_ssl_primary_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) -{ - dest->version = source->version; - dest->version_max = source->version_max; - dest->verifypeer = source->verifypeer; - dest->verifyhost = source->verifyhost; - dest->verifystatus = source->verifystatus; - dest->cache_session = source->cache_session; - dest->ssl_options = source->ssl_options; - - CLONE_BLOB(cert_blob); - CLONE_BLOB(ca_info_blob); - CLONE_BLOB(issuercert_blob); - CLONE_STRING(CApath); - CLONE_STRING(CAfile); - CLONE_STRING(issuercert); - CLONE_STRING(clientcert); - CLONE_STRING(cipher_list); - CLONE_STRING(cipher_list13); - CLONE_STRING(pinned_key); - CLONE_STRING(curves); - CLONE_STRING(signature_algorithms); - CLONE_STRING(CRLfile); -#ifdef USE_TLS_SRP - CLONE_STRING(username); - CLONE_STRING(password); -#endif - - return TRUE; -} - -static void free_primary_ssl_config(struct ssl_primary_config *sslc) -{ - curlx_safefree(sslc->CApath); - curlx_safefree(sslc->CAfile); - curlx_safefree(sslc->issuercert); - curlx_safefree(sslc->clientcert); - curlx_safefree(sslc->cipher_list); - curlx_safefree(sslc->cipher_list13); - curlx_safefree(sslc->pinned_key); - curlx_safefree(sslc->cert_blob); - curlx_safefree(sslc->ca_info_blob); - curlx_safefree(sslc->issuercert_blob); - curlx_safefree(sslc->curves); - curlx_safefree(sslc->signature_algorithms); - curlx_safefree(sslc->CRLfile); -#ifdef USE_TLS_SRP - curlx_safefree(sslc->username); - curlx_safefree(sslc->password); -#endif -} - -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) -{ - struct ssl_config_data *sslc = &data->set.ssl; -#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) - struct UserDefined *set = &data->set; - CURLcode result; -#endif - - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) - if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; -#endif -#ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); - if(result) - return result; - } -#endif -#ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); - if(result) - return result; - } -#endif - } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; - sslc->primary.signature_algorithms = - data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; - sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#endif - sslc->cert_type = data->set.str[STRING_CERT_TYPE]; - sslc->key = data->set.str[STRING_KEY]; - sslc->key_type = data->set.str[STRING_KEY_TYPE]; - sslc->key_passwd = data->set.str[STRING_KEY_PASSWD]; - sslc->primary.clientcert = data->set.str[STRING_CERT]; - sslc->key_blob = data->set.blobs[BLOB_KEY]; - -#ifndef CURL_DISABLE_PROXY - sslc = &data->set.proxy_ssl; - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) - if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) - sslc->native_ca_store = TRUE; -#endif -#ifdef CURL_CA_PATH - if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], - CURL_CA_PATH); - if(result) - return result; - } -#endif -#ifdef CURL_CA_BUNDLE - if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], - CURL_CA_BUNDLE); - if(result) - return result; - } -#endif - } - sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; - sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; - sslc->cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - sslc->key = data->set.str[STRING_KEY_PROXY]; - sslc->key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - sslc->key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; - sslc->key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#ifdef USE_TLS_SRP - sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif -#endif /* CURL_DISABLE_PROXY */ - - return CURLE_OK; -} - -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Clone "primary" SSL configurations from the easy handle to - * the connection. They are used for connection cache matching and - * probably outlive the easy handle */ - if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) - return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_PROXY - if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) - return CURLE_OUT_OF_MEMORY; -#endif - return CURLE_OK; -} - -void Curl_ssl_conn_config_cleanup(struct connectdata *conn) -{ - free_primary_ssl_config(&conn->ssl_config); -#ifndef CURL_DISABLE_PROXY - free_primary_ssl_config(&conn->proxy_ssl_config); -#endif -} - -void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) -{ - /* May be called on an easy that has no connection yet */ - if(data->conn) { - struct ssl_primary_config *src, *dest; -#ifndef CURL_DISABLE_PROXY - src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; - dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; -#else - (void)for_proxy; - src = &data->set.ssl.primary; - dest = &data->conn->ssl_config; -#endif - dest->verifyhost = src->verifyhost; - dest->verifypeer = src->verifypeer; - dest->verifystatus = src->verifystatus; - } -} - #ifdef USE_SSL static int multissl_setup(const struct Curl_ssl *backend); #endif @@ -617,8 +307,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data) ci->certinfo[i] = NULL; } - curlx_free(ci->certinfo); /* free the actual array too */ - ci->certinfo = NULL; + curlx_safefree(ci->certinfo); /* free the actual array too */ ci->num_of_certs = 0; } } @@ -1180,12 +869,11 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { + Curl_peer_unlink(&peer->origin); + Curl_peer_unlink(&peer->peer); curlx_safefree(peer->sni); - if(peer->dispname != peer->hostname) - curlx_free(peer->dispname); - peer->dispname = NULL; - curlx_safefree(peer->hostname); curlx_safefree(peer->scache_key); + peer->transport = TRNSPRT_NONE; peer->type = CURL_SSL_PEER_DNS; } @@ -1195,6 +883,8 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl) { connssl->ssl_impl->close(cf, data); connssl->state = ssl_connection_none; + connssl->connecting_state = ssl_connect_1; + connssl->prefs_checked = FALSE; Curl_ssl_peer_cleanup(&connssl->peer); } cf->connected = FALSE; @@ -1219,76 +909,49 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport) { - const char *ehostname, *edispname; CURLcode result = CURLE_OUT_OF_MEMORY; /* We expect a clean struct, e.g. called only ONCE */ - DEBUGASSERT(peer); - DEBUGASSERT(!peer->hostname); - DEBUGASSERT(!peer->dispname); - DEBUGASSERT(!peer->sni); - /* We need the hostname for SNI negotiation. Once handshaked, this remains - * the SNI hostname for the TLS connection. When the connection is reused, - * the settings in cf->conn might change. We keep a copy of the hostname we - * use for SNI. - */ - peer->transport = transport; -#ifndef CURL_DISABLE_PROXY - if(Curl_ssl_cf_is_proxy(cf)) { - ehostname = cf->conn->http_proxy.host.name; - edispname = cf->conn->http_proxy.host.dispname; - peer->port = cf->conn->http_proxy.port; - } - else -#endif - { - ehostname = cf->conn->host.name; - edispname = cf->conn->host.dispname; - peer->port = (uint16_t)cf->conn->remote_port; - } - - /* hostname MUST exist and not be empty */ - if(!ehostname || !ehostname[0]) { - result = CURLE_FAILED_INIT; - goto out; + if(!ssl_peer || !origin) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } + DEBUGASSERT(!ssl_peer->origin); + DEBUGASSERT(!ssl_peer->peer); + DEBUGASSERT(!ssl_peer->sni); + ssl_peer->transport = transport; - peer->hostname = curlx_strdup(ehostname); - if(!peer->hostname) - goto out; - if(!edispname || !strcmp(ehostname, edispname)) - peer->dispname = peer->hostname; - else { - peer->dispname = curlx_strdup(edispname); - if(!peer->dispname) - goto out; - } - peer->type = get_peer_type(peer->hostname); - if(peer->type == CURL_SSL_PEER_DNS) { + Curl_peer_link(&ssl_peer->origin, origin); + Curl_peer_link(&ssl_peer->peer, peer); + ssl_peer->type = get_peer_type(origin->hostname); + if(ssl_peer->type == CURL_SSL_PEER_DNS) { /* not an IP address, normalize according to RCC 6066 ch. 3, * max len of SNI is 2^16-1, no trailing dot */ - size_t len = strlen(peer->hostname); - if(len && (peer->hostname[len - 1] == '.')) + size_t len = strlen(origin->hostname); + if(len && (origin->hostname[len - 1] == '.')) len--; if(len < USHRT_MAX) { - peer->sni = curlx_calloc(1, len + 1); - if(!peer->sni) + ssl_peer->sni = curlx_calloc(1, len + 1); + if(!ssl_peer->sni) goto out; - Curl_strntolower(peer->sni, peer->hostname, len); - peer->sni[len] = 0; + Curl_strntolower(ssl_peer->sni, origin->hostname, len); + ssl_peer->sni[len] = 0; } } - result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key); + result = Curl_ssl_peer_key_make(ssl_peer, sslc, tls_id, + &ssl_peer->scache_key); out: if(result) - Curl_ssl_peer_cleanup(peer); + Curl_ssl_peer_cleanup(ssl_peer); return result; } @@ -1303,18 +966,6 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf->ctx = NULL; } -static void ssl_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - if(cf->next) - cf->next->cft->do_close(cf->next, data); - CF_DATA_RESTORE(cf, save); -} - static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1328,7 +979,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - if(!cf->next) { + if(!cf->next || !connssl->peer.origin) { *done = FALSE; return CURLE_FAILED_INIT; } @@ -1353,14 +1004,6 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->prefs_checked = TRUE; } - if(!connssl->peer.hostname) { - char tls_id[80]; - connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); - result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); - if(result) - goto out; - } - result = connssl->ssl_impl->do_connect(cf, data, done); if(!result && *done) { @@ -1382,7 +1025,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->earlydata_state > ssl_earlydata_none); } out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1572,7 +1215,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = connssl->ssl_impl->shut_down(cf, data, TRUE, done); - CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); cf->shutdown = (result || *done); } @@ -1676,7 +1319,6 @@ struct Curl_cftype Curl_cft_ssl = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1696,7 +1338,6 @@ struct Curl_cftype Curl_cft_ssl_proxy = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1745,28 +1386,53 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, return result; } +static CURLcode cf_ssl_peer_init(struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) +{ + struct ssl_connect_data *connssl = cf->ctx; + char tls_id[80]; + connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); + return Curl_ssl_peer_init(&connssl->peer, origin, peer, sslc, + tls_id, TRNSPRT_TCP); +} + CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex) { struct Curl_cfilter *cf; + struct Curl_peer *peer = (sockindex == SECONDARYSOCKET) ? + conn->via_peer2 : conn->via_peer; CURLcode result; result = cf_ssl_create(&cf, data, conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &conn->ssl_config); if(!result) Curl_conn_cf_add(data, conn, sockindex, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } @@ -1791,7 +1457,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } #endif - ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, FALSE, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1806,14 +1472,19 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_proxy_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, peer, NULL, &cf_at->conn->proxy_ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } @@ -1896,7 +1567,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, if(!result && !done) /* blocking failed? */ result = CURLE_SSL_SHUTDOWN_FAILED; Curl_conn_cf_discard(&cf, data); - CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); + CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", + (int)result); break; } } @@ -1908,8 +1580,8 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); } -struct ssl_config_data * -Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data) { #ifdef CURL_DISABLE_PROXY (void)cf; @@ -1919,8 +1591,8 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) #endif } -struct ssl_primary_config * -Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf) { #ifdef CURL_DISABLE_PROXY return &cf->conn->ssl_config; diff --git a/deps/curl/lib/vtls/vtls.h b/deps/curl/lib/vtls/vtls.h index 6db67cf748..96cb7d15ed 100644 --- a/deps/curl/lib/vtls/vtls.h +++ b/deps/curl/lib/vtls/vtls.h @@ -49,15 +49,13 @@ struct dynbuf; #define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -/* CURLECH_ bits for the tls_ech option */ -#define CURLECH_DISABLE (1 << 0) -#define CURLECH_GREASE (1 << 1) -#define CURLECH_ENABLE (1 << 2) -#define CURLECH_HARD (1 << 3) -#define CURLECH_CLA_CFG (1 << 4) - -#define CURLECH_ENABLED(data) \ - ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) +/* CURLECH_ values for the tls_ech option */ +#define CURLECH_DISABLE 0 +#define CURLECH_GREASE 1 +#define CURLECH_ENABLE 2 +#define CURLECH_HARD 3 + +#define CURLECH_ENABLED(data) ((data)->set.tls_ech) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -91,12 +89,11 @@ typedef enum { } ssl_peer_type; struct ssl_peer { - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ + struct Curl_peer *origin; /* the authority we talk to */ + struct Curl_peer *peer; /* the machine we are connected to */ char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ - uint16_t port; /* port we are talking to */ uint8_t transport; /* one of TRNSPRT_* defines */ }; @@ -107,48 +104,13 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, curl_sslbackend Curl_ssl_backend(void); -/** - * Init ssl config for a new easy handle. - */ -void Curl_ssl_easy_config_init(struct Curl_easy *data); - -/** - * Init the `data->set.ssl` and `data->set.proxy_ssl` for - * connection matching use. - */ -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); - -/** - * Init SSL configs (main + proxy) for a new connection from the easy handle. - */ -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn); - -/** - * Free allocated resources in SSL configs (main + proxy) for - * the given connection. - */ -void Curl_ssl_conn_config_cleanup(struct connectdata *conn); - -/** - * Return TRUE iff SSL configuration from `data` is functionally the - * same as the one on `candidate`. - * @param proxy match the proxy SSL config or the main one - */ -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy); - -/* Update certain connection SSL config flags after they have - * been changed on the easy handle. Works for `verifypeer`, - * `verifyhost` and `verifystatus`. */ -void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); - /** * Init SSL peer information for filter. Can be called repeatedly. */ -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport); /** @@ -194,7 +156,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void); /* The maximum size of the SSL channel binding is 85 bytes, as defined in - * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long, + * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes, * and SHA-512 is the longest supported hash algorithm, with a digest length of * 64 bytes. * The maximum size of the channel binding is therefore 21 + 64 = 85 bytes. @@ -215,18 +177,22 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex); CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer); CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, int sockindex, bool send_shutdown); #ifndef CURL_DISABLE_PROXY CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *peer); #endif /* !CURL_DISABLE_PROXY */ /** @@ -245,8 +211,8 @@ struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, /** * Get the primary config relevant for the filter from its connection. */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf); extern struct Curl_cftype Curl_cft_ssl; #ifndef CURL_DISABLE_PROXY @@ -266,7 +232,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_random(x, y, z) ((void)(x), CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE #define Curl_ssl_supports(a, b) FALSE -#define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN +#define Curl_ssl_cfilter_add(a, b, c, d) CURLE_NOT_BUILT_IN #define Curl_ssl_cfilter_remove(a, b, c) CURLE_OK #define Curl_ssl_cf_get_config(a, b) NULL #define Curl_ssl_cf_get_primary_config(a) NULL diff --git a/deps/curl/lib/vtls/vtls_config.c b/deps/curl/lib/vtls/vtls_config.c new file mode 100644 index 0000000000..4a9b69654b --- /dev/null +++ b/deps/curl/lib/vtls/vtls_config.c @@ -0,0 +1,423 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +/* This file is for implementing all "generic" SSL functions that all libcurl + internals should use. It is then responsible for calling the proper + "backend" function. + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + + Note that this source code uses the functions of the configured SSL + backend via the global Curl_ssl instance. + + "SSL/TLS Strong Encryption: An Introduction" + https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html +*/ + +#include "curl_setup.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "urldata.h" +#include "setopt.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "vtls/vtls_config.h" + + +#define CLONE_STRING(var) \ + do { \ + if(source->var) { \ + dest->var = curlx_strdup(source->var); \ + if(!dest->var) \ + return FALSE; \ + } \ + else \ + dest->var = NULL; \ + } while(0) + +#define CLONE_BLOB(var) \ + do { \ + if(blobdup(&dest->var, source->var)) \ + return FALSE; \ + } while(0) + +static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src) +{ + DEBUGASSERT(dest); + DEBUGASSERT(!*dest); + if(src) { + /* only if there is data to dupe! */ + struct curl_blob *d; + d = curlx_malloc(sizeof(struct curl_blob) + src->len); + if(!d) + return CURLE_OUT_OF_MEMORY; + d->len = src->len; + /* Always duplicate because the connection may survive longer than the + handle that passed in the blob. */ + d->flags = CURL_BLOB_COPY; + d->data = (void *)((char *)d + sizeof(struct curl_blob)); + memcpy(d->data, src->data, src->len); + *dest = d; + } + return CURLE_OK; +} + +/* returns TRUE if the blobs are identical */ +static bool blobcmp(struct curl_blob *first, struct curl_blob *second) +{ + if(!first && !second) /* both are NULL */ + return TRUE; + if(!first || !second) /* one is NULL */ + return FALSE; + if(first->len != second->len) /* different sizes */ + return FALSE; + return !memcmp(first->data, second->data, first->len); /* same data */ +} + +void Curl_ssl_config_init(struct ssl_primary_config *sslc) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + sslc->verifypeer = TRUE; + sslc->verifyhost = TRUE; + sslc->cache_session = TRUE; /* caching by default */ +} + +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc) +{ + if(sslc->deep_copy) { + curlx_safefree(sslc->CApath); + curlx_safefree(sslc->CAfile); + curlx_safefree(sslc->issuercert); + curlx_safefree(sslc->clientcert); + curlx_safefree(sslc->cipher_list); + curlx_safefree(sslc->cipher_list13); + curlx_safefree(sslc->pinned_key); + curlx_safefree(sslc->cert_blob); + curlx_safefree(sslc->ca_info_blob); + curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->key_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); + curlx_safefree(sslc->cert_type); + curlx_safefree(sslc->key); + curlx_safefree(sslc->key_type); + curlx_safefree(sslc->key_passwd); +#ifdef USE_TLS_SRP + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); +#endif + sslc->deep_copy = FALSE; + } +} + +static bool match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) +{ + (void)data; + if((c1->version == c2->version) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + blobcmp(c1->key_blob, c2->key_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && +#ifdef USE_TLS_SRP + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && +#endif + curl_strequal(c1->cipher_list, c2->cipher_list) && + curl_strequal(c1->cipher_list13, c2->cipher_list13) && + curl_strequal(c1->curves, c2->curves) && + curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && + Curl_safecmp(c1->CRLfile, c2->CRLfile) && + Curl_safecmp(c1->pinned_key, c2->pinned_key) && + curl_strequal(c1->cert_type, c2->cert_type) && + Curl_safecmp(c1->key, c2->key) && + curl_strequal(c1->key_type, c2->key_type) && + !Curl_timestrcmp(c1->key_passwd, c2->key_passwd)) + return TRUE; + + return FALSE; +} + +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) +{ + DEBUGASSERT(!dest->deep_copy); + dest->deep_copy = TRUE; + dest->version = source->version; + dest->version_max = source->version_max; + dest->verifypeer = source->verifypeer; + dest->verifyhost = source->verifyhost; + dest->verifystatus = source->verifystatus; + dest->cache_session = source->cache_session; + dest->ssl_options = source->ssl_options; + + CLONE_BLOB(cert_blob); + CLONE_BLOB(ca_info_blob); + CLONE_BLOB(issuercert_blob); + CLONE_STRING(CApath); + CLONE_STRING(CAfile); + CLONE_STRING(issuercert); + CLONE_STRING(cipher_list); + CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); + CLONE_STRING(curves); + CLONE_STRING(signature_algorithms); + CLONE_STRING(CRLfile); + /* SSL credentials: client certificate, SRP auth */ + CLONE_STRING(clientcert); + CLONE_STRING(cert_type); + CLONE_STRING(key); + CLONE_STRING(key_type); + CLONE_STRING(key_passwd); + CLONE_BLOB(key_blob); +#ifdef USE_TLS_SRP + CLONE_STRING(username); + CLONE_STRING(password); +#endif + + return TRUE; +} + +static void ssl_easy_config_compl_options(struct Curl_peer *origin, + struct Curl_peer *initial_origin, + struct ssl_config_data *sslc) +{ + uint8_t options = sslc->primary.ssl_options; + /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it. + * If not, we switch it on for supported backends if no custom + * CA settings exist. */ + sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); + sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST); + sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN); + sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE); + sslc->revoke_best_effort = !!(options & CURLSSLOPT_REVOKE_BEST_EFFORT); + sslc->earlydata = !!(options & CURLSSLOPT_EARLYDATA); + + sslc->auto_client_cert = Curl_peer_equal(origin, initial_origin) && + !!(options & CURLSSLOPT_AUTO_CLIENT_CERT); +} + +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin) +{ + struct ssl_config_data *sslc = &data->set.ssl; +#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) + struct UserDefined *set = &data->set; + CURLcode result; +#endif + + ssl_easy_config_compl_options(origin, data->state.initial_origin, sslc); + + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) + if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) + sslc->native_ca_store = TRUE; +#endif +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; + sslc->primary.signature_algorithms = + data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; + /* Maybe these should not be used for another origin. But for + * backwards compatibility, keep them in. */ + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + + if(Curl_peer_equal(data->state.initial_origin, origin)) { + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; + sslc->primary.key = data->set.str[STRING_KEY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; + sslc->primary.clientcert = data->set.str[STRING_CERT]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + } + else { + sslc->primary.pinned_key = NULL; + sslc->primary.cert_blob = NULL; + sslc->primary.cert_type = NULL; + sslc->primary.key = NULL; + sslc->primary.key_type = NULL; + sslc->primary.key_passwd = NULL; + sslc->primary.clientcert = NULL; + sslc->primary.key_blob = NULL; +#ifdef USE_TLS_SRP + sslc->primary.username = NULL; + sslc->primary.password = NULL; +#endif + } + +#ifndef CURL_DISABLE_PROXY + sslc = &data->set.proxy_ssl; + /* no initial origin for proxy, it is not changed for redirects */ + ssl_easy_config_compl_options(NULL, NULL, sslc); + + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE) + if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob) + sslc->native_ca_store = TRUE; +#endif +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], + CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], + CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + sslc->primary.key = data->set.str[STRING_KEY_PROXY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; +#endif +#endif /* CURL_DISABLE_PROXY */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the easy handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_ssl_config_cleanup(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} diff --git a/deps/curl/lib/vtls/vtls_config.h b/deps/curl/lib/vtls/vtls_config.h new file mode 100644 index 0000000000..44e691dd26 --- /dev/null +++ b/deps/curl/lib/vtls/vtls_config.h @@ -0,0 +1,125 @@ +#ifndef HEADER_CURL_VTLS_CONFIG_H +#define HEADER_CURL_VTLS_CONFIG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct Curl_peer; + +struct ssl_primary_config { + char *CApath; /* certificate directory (does not work on Windows) */ + char *CAfile; /* certificate to verify peer against */ + char *issuercert; /* optional issuer certificate filename */ + char *clientcert; + char *cipher_list; /* list of ciphers to use */ + char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ + char *signature_algorithms; /* list of signature algorithms to use */ + char *pinned_key; + char *CRLfile; /* CRL to check certificate revocation */ + char *cert_type; /* format for certificate (default: PEM) */ + char *key; /* private key filename */ + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ + struct curl_blob *cert_blob; + struct curl_blob *ca_info_blob; + struct curl_blob *issuercert_blob; + struct curl_blob *key_blob; +#ifdef USE_TLS_SRP + char *username; /* TLS username (for, e.g., SRP) */ + char *password; /* TLS password (for, e.g., SRP) */ +#endif + char *curves; /* list of curves to use */ + uint32_t version_max; /* max supported version the client wants to use */ + uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + uint8_t version; /* what version the client wants to use */ + BIT(verifypeer); /* set TRUE if this is desired */ + BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ + BIT(verifystatus); /* set TRUE if certificate status must be checked */ + BIT(cache_session); /* cache session or not */ + BIT(deep_copy); /* members are deep copies, eg. owned here */ +}; + +struct ssl_config_data { + struct ssl_primary_config primary; + long certverifyresult; /* result from the certificate verification */ + curl_ssl_ctx_callback fsslctx; /* function to initialize SSL ctx */ + void *fsslctxp; /* parameter for call back */ + BIT(certinfo); /* gather lots of certificate info */ + BIT(earlydata); /* use TLS 1.3 early data */ + BIT(enable_beast); /* allow this flaw for interoperability's sake */ + BIT(no_revoke); /* disable SSL certificate revocation checks */ + BIT(no_partialchain); /* do not accept partial certificate chains */ + BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation + list errors */ + BIT(native_ca_store); /* use the native CA store of operating system */ + BIT(auto_client_cert); /* automatically locate and use a client + certificate for authentication (Schannel) */ + BIT(custom_cafile); /* application has set custom CA file */ + BIT(custom_capath); /* application has set custom CA path */ + BIT(custom_cablob); /* application has set custom CA blob */ +}; + +struct ssl_general_config { + int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ +}; + +void Curl_ssl_config_init(struct ssl_primary_config *sslc); +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `data` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * been changed on the easy handle. Works for `verifypeer`, + * `verifyhost` and `verifystatus`. */ +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); + +#endif /* HEADER_CURL_VTLS_CONFIG_H */ diff --git a/deps/curl/lib/vtls/vtls_int.h b/deps/curl/lib/vtls/vtls_int.h index 6700ee74cb..fa5da6e4d5 100644 --- a/deps/curl/lib/vtls/vtls_int.h +++ b/deps/curl/lib/vtls/vtls_int.h @@ -38,20 +38,20 @@ struct Curl_ssl_session; /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_0_LENGTH 8 -#define ALPN_HTTP_1_0 "http/1.0" +#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_H2_LENGTH 2 -#define ALPN_H2 "h2" -#define ALPN_H3_LENGTH 2 -#define ALPN_H3 "h3" +#define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_H2_LENGTH 2 +#define ALPN_H2 "h2" +#define ALPN_H3_LENGTH 2 +#define ALPN_H3 "h3" /* conservative sizes on the ALPN entries and count we are handling, * we can increase these if we ever feel the need or have to accommodate * ALPN strings from the "outside". */ -#define ALPN_NAME_MAX 10 -#define ALPN_ENTRIES_MAX 3 -#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) +#define ALPN_NAME_MAX 10 +#define ALPN_ENTRIES_MAX 3 +#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) struct alpn_spec { char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; @@ -133,9 +133,6 @@ struct ssl_connect_data { BIT(input_pending); /* data for SSL_read() may be available */ }; -#undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data - /* Definitions for SSL Implementations */ struct Curl_ssl { @@ -209,3 +206,9 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ + +#ifdef USE_SSL +/* Restore the default SSL filter call_data accessor for unity builds. */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data +#endif diff --git a/deps/curl/lib/vtls/vtls_scache.c b/deps/curl/lib/vtls/vtls_scache.c index 59fa256bc3..98beeac036 100644 --- a/deps/curl/lib/vtls/vtls_scache.c +++ b/deps/curl/lib/vtls/vtls_scache.c @@ -50,6 +50,7 @@ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ char *clientcert; + char *key_passwd; char *srp_username; char *srp_password; struct Curl_llist sessions; @@ -85,10 +86,10 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, if(path[0] != '/') { char *abspath = realpath(path, NULL); if(abspath) { - CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + CURLcode result = curlx_dyn_addf(buf, ":%s-%s", name, abspath); /* !checksrc! disable BANNEDFUNC 1 */ free(abspath); /* allocated by libc, free without memdebug */ - return r; + return result; } *is_local = TRUE; } @@ -102,25 +103,67 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, const char *name, struct curl_blob *blob) { - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; if(blob && blob->len) { unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; size_t i; - r = curlx_dyn_addf(buf, ":%s-", name); - if(r) + result = curlx_dyn_addf(buf, ":%s-", name); + if(result) goto out; - r = Curl_sha256it(hash, blob->data, blob->len); - if(r) + result = Curl_sha256it(hash, blob->data, blob->len); + if(result) goto out; for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { - r = curlx_dyn_addf(buf, "%02x", hash[i]); - if(r) + result = curlx_dyn_addf(buf, "%02x", hash[i]); + if(result) goto out; } } out: - return r; + return result; +} + +static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf, + struct ssl_primary_config *ssl, + bool *is_local) +{ + CURLcode result = CURLE_OK; + if(ssl->clientcert && ssl->clientcert[0]) { + result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local); + if(result) + goto out; + } + if(ssl->key && ssl->key[0]) { + result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local); + if(result) + goto out; + } + if(ssl->key_blob) { + result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob); + if(result) + goto out; + } + if(ssl->cert_type && ssl->cert_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":CT-"); + for(i = 0; !result && ssl->cert_type[i]; i++) { + char c = Curl_raw_toupper(ssl->cert_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + if(result) + goto out; + } + if(ssl->key_type && ssl->key_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":KT-"); + for(i = 0; !result && ssl->key_type[i]; i++) { + char c = Curl_raw_toupper(ssl->key_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + } +out: + return result; } #define CURL_SSLS_LOCAL_SUFFIX ":L" @@ -134,156 +177,169 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key) (peer_key[len - 2] == ':'); } -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, - const char *tls_id, - char **ppeer_key) +static CURLcode ssl_peer_key_add_transport(struct dynbuf *buf, + uint8_t transport) { - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); - struct dynbuf buf; - size_t key_len; - bool is_local = FALSE; - CURLcode r; - - *ppeer_key = NULL; - curlx_dyn_init(&buf, 10 * 1024); - - r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); - if(r) - goto out; - - switch(peer->transport) { + switch(transport) { case TRNSPRT_TCP: - break; + return CURLE_OK; case TRNSPRT_UDP: - r = curlx_dyn_add(&buf, ":UDP"); - break; + return curlx_dyn_add(buf, ":UDP"); case TRNSPRT_QUIC: - r = curlx_dyn_add(&buf, ":QUIC"); - break; + return curlx_dyn_add(buf, ":QUIC"); case TRNSPRT_UNIX: - r = curlx_dyn_add(&buf, ":UNIX"); - break; + return curlx_dyn_add(buf, ":UNIX"); default: - r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); - break; + return curlx_dyn_addf(buf, ":TRNSPRT-%d", transport); } - if(r) - goto out; +} + +static CURLcode ssl_peer_key_add_vrfy(struct dynbuf *buf, + struct ssl_primary_config *ssl, + const struct ssl_peer *peer) +{ + CURLcode result; if(!ssl->verifypeer) { - r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":NO-VRFY-PEER"); + if(result) + return result; } if(!ssl->verifyhost) { - r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":NO-VRFY-HOST"); + if(result) + return result; } if(ssl->verifystatus) { - r = curlx_dyn_add(&buf, ":VRFY-STATUS"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":VRFY-STATUS"); + if(result) + return result; } - if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->bits.conn_to_host) { - r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); - if(r) - goto out; - } - if(cf->conn->bits.conn_to_port) { - r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); - if(r) - goto out; - } + if((!ssl->verifypeer || !ssl->verifyhost) && + peer->peer && !Curl_peer_equal(peer->origin, peer->peer)) { + result = curlx_dyn_addf(buf, ":CHOST-%s:CPORT-%u", + peer->peer->hostname, + peer->peer->port); + if(result) + return result; } + return CURLE_OK; +} +static CURLcode ssl_peer_key_build(struct ssl_primary_config *ssl, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct dynbuf buf; + size_t key_len; + bool is_local = FALSE; + CURLcode result; + + *ppeer_key = NULL; + curlx_dyn_init(&buf, 10 * 1024); + + result = curlx_dyn_addf(&buf, "%s:%d", + peer->origin->hostname, peer->origin->port); + if(result) + goto out; + result = ssl_peer_key_add_transport(&buf, peer->transport); + if(result) + goto out; + result = ssl_peer_key_add_vrfy(&buf, ssl, peer); + if(result) + goto out; if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, - (ssl->version_max >> 16)); - if(r) + result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, + (ssl->version_max >> 16)); + if(result) goto out; } if(ssl->ssl_options) { - r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); - if(r) + result = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(result) goto out; } if(ssl->cipher_list) { - r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(result) goto out; } if(ssl->cipher_list13) { - r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(result) goto out; } if(ssl->curves) { - r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); - if(r) + result = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(result) + goto out; + } + if(ssl->signature_algorithms) { + result = curlx_dyn_addf(&buf, ":SIGALGS-%s", + ssl->signature_algorithms); + if(result) goto out; } if(ssl->verifypeer) { - r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, + &is_local); + if(result) goto out; if(ssl->ca_info_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(result) goto out; } if(ssl->issuercert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", + ssl->issuercert_blob); + if(result) goto out; } } if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(result) goto out; } if(ssl->pinned_key && ssl->pinned_key[0]) { - r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); - if(r) + result = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(result) goto out; } - if(ssl->clientcert && ssl->clientcert[0]) { - r = curlx_dyn_add(&buf, ":CCERT"); - if(r) - goto out; - } + result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local); + if(result) + goto out; #ifdef USE_TLS_SRP if(ssl->username || ssl->password) { - r = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(r) + result = curlx_dyn_add(&buf, ":SRP-AUTH"); + if(result) goto out; } #endif if(!tls_id || !tls_id[0]) { - r = CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; goto out; } - r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); - if(r) + result = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(result) goto out; - r = curlx_dyn_addf(&buf, is_local ? - CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); - if(r) + result = curlx_dyn_addf(&buf, is_local ? + CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); + if(result) goto out; *ppeer_key = curlx_dyn_take(&buf, &key_len); @@ -292,7 +348,15 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, out: curlx_dyn_free(&buf); - return r; + return result; +} + +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, + const char *tls_id, + char **ppeer_key) +{ + return ssl_peer_key_build(sslc, peer, tls_id, ppeer_key); } struct Curl_ssl_scache { @@ -403,6 +467,7 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) } peer->sobj_free = NULL; curlx_safefree(peer->clientcert); + curlx_safefree(peer->key_passwd); #ifdef USE_TLS_SRP curlx_safefree(peer->srp_username); curlx_safefree(peer->srp_password); @@ -431,20 +496,20 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) * - its peer key is not yet known, because sessions were * imported using only the salt+hmac * - the peer key is global, e.g. carrying no relative paths */ - peer->exportable = (!peer->clientcert && !peer->srp_username && - !peer->srp_password && + peer->exportable = (!peer->clientcert && !peer->key_passwd && + !peer->srp_username && !peer->srp_password && (!peer->ssl_peer_key || cf_ssl_peer_key_is_global(peer->ssl_peer_key))); } -static CURLcode -cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, - const char *ssl_peer_key, - const char *clientcert, - const char *srp_username, - const char *srp_password, - const unsigned char *salt, - const unsigned char *hmac) +static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, + const char *ssl_peer_key, + const char *clientcert, + const char *key_passwd, + const char *srp_username, + const char *srp_password, + const unsigned char *salt, + const unsigned char *hmac) { CURLcode result = CURLE_OUT_OF_MEMORY; @@ -469,6 +534,11 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, if(!peer->clientcert) goto out; } + if(key_passwd) { + peer->key_passwd = curlx_strdup(key_passwd); + if(!peer->key_passwd) + goto out; + } if(srp_username) { peer->srp_username = curlx_strdup(srp_username); if(!peer->srp_username) @@ -610,7 +680,7 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { if(!conn_config) { - if(peer->clientcert) + if(peer->clientcert || peer->key_passwd) return FALSE; #ifdef USE_TLS_SRP if(peer->srp_username || peer->srp_password) @@ -620,6 +690,8 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, } else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) return FALSE; + if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd)) + return FALSE; #ifdef USE_TLS_SRP if(Curl_timestrcmp(peer->srp_username, conn_config->username) || Curl_timestrcmp(peer->srp_password, conn_config->password)) @@ -692,8 +764,8 @@ static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data, return result; } -static struct Curl_ssl_scache_peer * -cf_ssl_get_free_peer(struct Curl_ssl_scache *scache) +static struct Curl_ssl_scache_peer *cf_ssl_get_free_peer( + struct Curl_ssl_scache *scache) { struct Curl_ssl_scache_peer *peer = NULL; size_t i; @@ -748,6 +820,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, if(peer) { char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; + const char *kpasswd = conn_config ? conn_config->key_passwd : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; @@ -759,7 +832,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, "cert-%p", conn_config->cert_blob->data); ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ } - result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd, username, password, NULL, NULL); if(result) goto out; @@ -829,7 +902,7 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); Curl_ssl_session_destroy(s); goto out; } @@ -839,13 +912,13 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, out: if(result) { failf(data, "[SCACHE] failed to add session for %s, error=%d", - ssl_peer_key, result); + ssl_peer_key, (int)result); } else CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, " "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, " - "quic_tp=%s], peer has %zu sessions now", - ssl_peer_key, s->ietf_tls_id, s->valid_until - now, + "quic_tp=%s], peer has %zu sessions now", ssl_peer_key, + (unsigned int)s->ietf_tls_id, s->valid_until - now, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", peer ? Curl_llist_count(&peer->sessions) : 0); return result; @@ -917,7 +990,7 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, *ps = s; CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, " "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain", - ssl_peer_key, s->ietf_tls_id, s->alpn, + ssl_peer_key, (unsigned int)s->ietf_tls_id, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", Curl_llist_count(&peer->sessions)); } @@ -949,7 +1022,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); goto out; } @@ -1040,11 +1113,10 @@ static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) return result; } -static CURLcode -cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, - const unsigned char *salt, - const unsigned char *hmac, - struct Curl_ssl_scache_peer **ppeer) +static CURLcode cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, + const unsigned char *salt, + const unsigned char *hmac, + struct Curl_ssl_scache_peer **ppeer) { size_t i; CURLcode result = CURLE_OK; @@ -1138,7 +1210,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data, if(!peer) { peer = cf_ssl_get_free_peer(scache); if(peer) { - result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL, NULL, NULL, salt, hmac); if(result) goto out; @@ -1172,7 +1244,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, struct Curl_llist_node *n; size_t i; curl_off_t now = time(NULL); - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; #ifdef CURLVERBOSE size_t npeers = 0, ntickets = 0; #endif @@ -1188,7 +1260,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, for(i = 0; scache && i < scache->peer_count; i++) { peer = &scache->peers[i]; if(!peer->ssl_peer_key && !peer->hmac_set) - continue; /* skip free entry */ + continue; /* skip free entry */ if(!peer->exportable) continue; @@ -1200,35 +1272,35 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, while(n) { struct Curl_ssl_session *s = Curl_node_elem(n); if(!peer->hmac_set) { - r = cf_ssl_scache_peer_set_hmac(peer); - if(r) + result = cf_ssl_scache_peer_set_hmac(peer); + if(result) goto out; } if(!curlx_dyn_len(&hbuf)) { - r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); + if(result) goto out; - r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); + if(result) goto out; } curlx_dyn_reset(&sbuf); - r = Curl_ssl_session_pack(data, s, &sbuf); - if(r) + result = Curl_ssl_session_pack(data, s, &sbuf); + if(result) goto out; - r = export_fn(data, userptr, peer->ssl_peer_key, - curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), - curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), - s->valid_until, s->ietf_tls_id, - s->alpn, s->earlydata_max); - if(r) + result = export_fn(data, userptr, peer->ssl_peer_key, + curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), + curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), + s->valid_until, s->ietf_tls_id, + s->alpn, s->earlydata_max); + if(result) goto out; VERBOSE(++ntickets); n = Curl_node_next(n); } } - r = CURLE_OK; + result = CURLE_OK; CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers", ntickets, npeers); @@ -1236,7 +1308,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, Curl_ssl_scache_unlock(data); curlx_dyn_free(&hbuf); curlx_dyn_free(&sbuf); - return r; + return result; } #endif /* USE_SSLS_EXPORT */ diff --git a/deps/curl/lib/vtls/vtls_scache.h b/deps/curl/lib/vtls/vtls_scache.h index a6a36f1630..effb1d8f96 100644 --- a/deps/curl/lib/vtls/vtls_scache.h +++ b/deps/curl/lib/vtls/vtls_scache.h @@ -54,20 +54,20 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache); * connection to the peer. * If the filter is a TLS proxy filter, it uses the proxy relevant * information. - * @param cf the connection filter wanting to use it * @param peer the peer the filter wants to talk to + * @param sslc the relevant ssl configuration * @param tls_id identifier of TLS implementation for sessions. Should * include full version if session data from other versions * is to be avoided. * @param ppeer_key on successful return, the key generated */ -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, char **ppeer_key); /* Return if there is a session cache shall be used. - * An ssl session might not be configured or not available for + * An SSL session might not be configured or not available for * "connect-only" transfers. */ bool Curl_ssl_scache_use(struct Curl_cfilter *cf, struct Curl_easy *data); diff --git a/deps/curl/lib/vtls/vtls_spack.c b/deps/curl/lib/vtls/vtls_spack.c index d96f4e41bd..9cbdecc901 100644 --- a/deps/curl/lib/vtls/vtls_spack.c +++ b/deps/curl/lib/vtls/vtls_spack.c @@ -31,13 +31,6 @@ #include "vtls/vtls_spack.h" #include "curlx/strdup.h" -#ifndef UINT16_MAX -#define UINT16_MAX 0xffff -#endif -#ifndef UINT32_MAX -#define UINT32_MAX 0xffffffff -#endif - #define CURL_SPACK_VERSION 0x01 #define CURL_SPACK_IETF_ID 0x02 #define CURL_SPACK_VALID_UNTIL 0x03 @@ -130,26 +123,26 @@ static CURLcode spack_dec64(uint64_t *val, const uint8_t **src, static CURLcode spack_encstr16(struct dynbuf *buf, const char *s) { size_t slen = strlen(s); - CURLcode r; + CURLcode result; if(slen > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)slen); - if(!r) { - r = curlx_dyn_addn(buf, s, slen); + result = spack_enc16(buf, (uint16_t)slen); + if(!result) { + result = curlx_dyn_addn(buf, s, slen); } - return r; + return result; } static CURLcode spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) { uint16_t slen; - CURLcode r; + CURLcode result; *val = NULL; - r = spack_dec16(&slen, src, end); - if(r) - return r; + result = spack_dec16(&slen, src, end); + if(result) + return result; if(end - *src < slen) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), slen); @@ -160,26 +153,26 @@ static CURLcode spack_decstr16(char **val, const uint8_t **src, static CURLcode spack_encdata16(struct dynbuf *buf, const uint8_t *data, size_t data_len) { - CURLcode r; + CURLcode result; if(data_len > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)data_len); - if(!r) { - r = curlx_dyn_addn(buf, data, data_len); + result = spack_enc16(buf, (uint16_t)data_len); + if(!result) { + result = curlx_dyn_addn(buf, data, data_len); } - return r; + return result; } static CURLcode spack_decdata16(uint8_t **val, size_t *val_len, const uint8_t **src, const uint8_t *end) { uint16_t data_len; - CURLcode r; + CURLcode result; *val = NULL; - r = spack_dec16(&data_len, src, end); - if(r) - return r; + result = spack_dec16(&data_len, src, end); + if(result) + return result; if(end - *src < data_len) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), data_len); @@ -192,48 +185,48 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, struct Curl_ssl_session *s, struct dynbuf *buf) { - CURLcode r; + CURLcode result; DEBUGASSERT(s->sdata); DEBUGASSERT(s->sdata_len); if(s->valid_until < 0) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc8(buf, CURL_SPACK_VERSION); - if(!r) - r = spack_enc8(buf, CURL_SPACK_TICKET); - if(!r) - r = spack_encdata16(buf, s->sdata, s->sdata_len); - if(!r) - r = spack_enc8(buf, CURL_SPACK_IETF_ID); - if(!r) - r = spack_enc16(buf, (uint16_t)s->ietf_tls_id); - if(!r) - r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); - if(!r) - r = spack_enc64(buf, (uint64_t)s->valid_until); - if(!r && s->alpn) { - r = spack_enc8(buf, CURL_SPACK_ALPN); - if(!r) - r = spack_encstr16(buf, s->alpn); + result = spack_enc8(buf, CURL_SPACK_VERSION); + if(!result) + result = spack_enc8(buf, CURL_SPACK_TICKET); + if(!result) + result = spack_encdata16(buf, s->sdata, s->sdata_len); + if(!result) + result = spack_enc8(buf, CURL_SPACK_IETF_ID); + if(!result) + result = spack_enc16(buf, (uint16_t)s->ietf_tls_id); + if(!result) + result = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); + if(!result) + result = spack_enc64(buf, (uint64_t)s->valid_until); + if(!result && s->alpn) { + result = spack_enc8(buf, CURL_SPACK_ALPN); + if(!result) + result = spack_encstr16(buf, s->alpn); } - if(!r && s->earlydata_max) { + if(!result && s->earlydata_max) { if(s->earlydata_max > UINT32_MAX) - r = CURLE_BAD_FUNCTION_ARGUMENT; - if(!r) - r = spack_enc8(buf, CURL_SPACK_EARLYDATA); - if(!r) - r = spack_enc32(buf, (uint32_t)s->earlydata_max); + result = CURLE_BAD_FUNCTION_ARGUMENT; + if(!result) + result = spack_enc8(buf, CURL_SPACK_EARLYDATA); + if(!result) + result = spack_enc32(buf, (uint32_t)s->earlydata_max); } - if(!r && s->quic_tp && s->quic_tp_len) { - r = spack_enc8(buf, CURL_SPACK_QUICTP); - if(!r) - r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); + if(!result && s->quic_tp && s->quic_tp_len) { + result = spack_enc8(buf, CURL_SPACK_QUICTP); + if(!result) + result = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); } - if(r) - CURL_TRC_SSLS(data, "error packing data: %d", r); - return r; + if(result) + CURL_TRC_SSLS(data, "error packing data: %d", (int)result); + return result; } CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, @@ -247,83 +240,83 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, uint16_t val16; uint32_t val32; uint64_t val64; - CURLcode r; + CURLcode result; DEBUGASSERT(buf); DEBUGASSERT(buflen); *ps = NULL; - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; if(val8 != CURL_SPACK_VERSION) { - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } s = curlx_calloc(1, sizeof(*s)); if(!s) { - r = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto out; } while(buf < end) { - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; switch(val8) { case CURL_SPACK_ALPN: - r = spack_decstr16(&s->alpn, &buf, end); - if(r) + result = spack_decstr16(&s->alpn, &buf, end); + if(result) goto out; break; case CURL_SPACK_EARLYDATA: - r = spack_dec32(&val32, &buf, end); - if(r) + result = spack_dec32(&val32, &buf, end); + if(result) goto out; s->earlydata_max = val32; break; case CURL_SPACK_IETF_ID: - r = spack_dec16(&val16, &buf, end); - if(r) + result = spack_dec16(&val16, &buf, end); + if(result) goto out; s->ietf_tls_id = val16; break; case CURL_SPACK_QUICTP: { - r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + if(result) goto out; s->quic_tp = pval8; break; } case CURL_SPACK_TICKET: { - r = spack_decdata16(&pval8, &s->sdata_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + if(result) goto out; s->sdata = pval8; break; } case CURL_SPACK_VALID_UNTIL: - r = spack_dec64(&val64, &buf, end); - if(r) + result = spack_dec64(&val64, &buf, end); + if(result) goto out; s->valid_until = (curl_off_t)val64; break; default: /* unknown tag */ - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } } out: - if(r) { - CURL_TRC_SSLS(data, "error unpacking data: %d", r); + if(result) { + CURL_TRC_SSLS(data, "error unpacking data: %d", (int)result); Curl_ssl_session_destroy(s); } else *ps = s; - return r; + return result; } #endif /* USE_SSL && USE_SSLS_EXPORT */ diff --git a/deps/curl/lib/vtls/wolfssl.c b/deps/curl/lib/vtls/wolfssl.c index 15c81c2874..92eaa7a751 100644 --- a/deps/curl/lib/vtls/wolfssl.c +++ b/deps/curl/lib/vtls/wolfssl.c @@ -81,7 +81,7 @@ options.h. */ #ifndef KEEP_PEER_CERT #if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ - (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) + (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) #define KEEP_PEER_CERT #endif #endif @@ -218,26 +218,6 @@ static int wssl_do_file_type(const char *type) return -1; } -#ifdef WOLFSSL_HAVE_KYBER -struct group_name_map { - const word16 group; - const char *name; -}; - -static const struct group_name_map gnm[] = { - { WOLFSSL_ML_KEM_512, "ML_KEM_512" }, - { WOLFSSL_ML_KEM_768, "ML_KEM_768" }, - { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" }, - { WOLFSSL_SECP256R1MLKEM512, "SecP256r1MLKEM512" }, - { WOLFSSL_SECP384R1MLKEM768, "SecP384r1MLKEM768" }, - { WOLFSSL_SECP521R1MLKEM1024, "SecP521r1MLKEM1024" }, - { WOLFSSL_SECP256R1MLKEM768, "SecP256r1MLKEM768" }, - { WOLFSSL_SECP384R1MLKEM1024, "SecP384r1MLKEM1024" }, - { WOLFSSL_X25519MLKEM768, "X25519MLKEM768" }, - { 0, NULL } -}; -#endif - #ifdef USE_BIO_CHAIN static int wssl_bio_cf_create(WOLFSSL_BIO *bio) @@ -316,14 +296,14 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) * sending during shutdown. */ CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d" " to %d bytes", blen, wssl->io_send_blocked_len); - skiplen = (ssize_t)(blen - wssl->io_send_blocked_len); + skiplen = (size_t)(blen - wssl->io_send_blocked_len); blen = wssl->io_send_blocked_len; } result = Curl_conn_cf_send(cf->next, data, (const uint8_t *)buf, blen, FALSE, &nwritten); wssl->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -360,7 +340,7 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) * server response. This allows sending of ClientHello without delay. */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); wssl->io_result = result; return -1; } @@ -368,7 +348,8 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); wssl->io_result = result; - CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, (int)result, + nread); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -480,7 +461,7 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) struct Curl_cfilter *cf; cf = (struct Curl_cfilter *)wolfSSL_get_app_data(ssl); - DEBUGASSERT(cf != NULL); + DEBUGASSERT(cf); if(cf && session) { struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); @@ -517,13 +498,13 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, connssl->earlydata_max); } -static CURLcode -wssl_setup_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct wssl_ctx *wss, - struct alpn_spec *alpns, - const char *ssl_peer_key, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode wssl_setup_session( + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wssl_ctx *wss, + struct alpn_spec *alpns, + const char *ssl_peer_key, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *scs = NULL; @@ -855,7 +836,7 @@ static CURLcode wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) { size_t n; - if((strncmp(str, "TLS13", 5) == 0) != tls13) + if((!strncmp(str, "TLS13", 5)) != tls13) continue; /* if there already is data in the string, add colon separator */ @@ -920,10 +901,10 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #ifndef NO_FILESYSTEM if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) { const char *cert_file = ssl_config->primary.clientcert; - const char *key_file = ssl_config->key; + const char *key_file = ssl_config->primary.key; const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -954,7 +935,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, key_file = cert_file; } else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); rc = key_blob ? wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, @@ -968,8 +949,8 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #else /* NO_FILESYSTEM */ if(ssl_config->primary.cert_blob) { const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -994,7 +975,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, if(!key_blob) key_blob = cert_blob; else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, (long)key_blob->len, @@ -1076,7 +1057,6 @@ static CURLcode ssl_version(struct Curl_easy *data, return CURLE_OK; } -#define QUIC_GROUPS "P-256:P-384:P-521" #ifdef WOLFSSL_TLS13 #define MAX_CIPHER_LEN 4096 #endif @@ -1137,53 +1117,39 @@ static CURLcode wssl_init_ciphers(struct Curl_easy *data, #endif } +/* wolfSSL_CTX_set1_groups_list() accepts PQC/hybrid groups (e.g. + X25519MLKEM768) that wolfSSL_CTX_set1_curves_list() rejects. It needs + OPENSSL_EXTRA, which wolfSSL's --enable-curl sets but --enable-curl=tiny + does not. */ +#ifdef OPENSSL_EXTRA +#define wssl_CTX_set1_groups_list wolfSSL_CTX_set1_groups_list +#else +#define wssl_CTX_set1_groups_list wolfSSL_CTX_set1_curves_list +#endif + static CURLcode wssl_init_curves(struct Curl_easy *data, struct wssl_ctx *wctx, - struct ssl_primary_config *conn_config, - unsigned char transport -#ifdef WOLFSSL_HAVE_KYBER - , word16 *out_pqkem -#endif - ) + struct ssl_primary_config *conn_config) { char *curves = conn_config->curves; - if(!curves && (transport == TRNSPRT_QUIC)) - curves = (char *)CURL_UNCONST(QUIC_GROUPS); - - if(curves) { -#ifdef WOLFSSL_HAVE_KYBER - size_t idx; - for(idx = 0; gnm[idx].name != NULL; idx++) { - if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) { - *out_pqkem = gnm[idx].group; - break; - } - } - - if(*out_pqkem == 0) -#endif - { - if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) { - failf(data, "failed setting curves list: '%s'", curves); - return CURLE_SSL_CIPHER; - } - } + /* Without an explicit list, leave the key share group selection to + wolfSSL's own default. */ + if(curves && !wssl_CTX_set1_groups_list(wctx->ssl_ctx, curves)) { + failf(data, "failed setting curves list: '%s'", curves); + return CURLE_SSL_CIPHER; } return CURLE_OK; } -static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - struct alpn_spec *alpns, - void *ssl_user_data, - unsigned char transport, -#ifdef WOLFSSL_HAVE_KYBER - word16 pqkem, -#endif - Curl_wssl_init_session_reuse_cb - *sess_reuse_cb) +static CURLcode wssl_init_ssl_handle( + struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + void *ssl_user_data, + unsigned char transport, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { /* Let's make an SSL structure */ wctx->ssl = wolfSSL_new(wctx->ssl_ctx); @@ -1204,15 +1170,6 @@ static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, (void)transport; #endif -#ifdef WOLFSSL_HAVE_KYBER - if(pqkem) { - if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != - WOLFSSL_SUCCESS) { - failf(data, "unable to use PQ KEM"); - } - } -#endif - /* Check if there is a cached ID we can/should use here! */ if(Curl_ssl_scache_use(cf, data)) { /* Set session from cache if there is one */ @@ -1260,7 +1217,8 @@ static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG static CURLcode wssl_init_ech(struct wssl_ctx *wctx, struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct ssl_peer *peer) { int trying_ech_now = 0; @@ -1273,15 +1231,14 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, infof(data, "ECH: GREASE is done by default by" " wolfSSL: no need to ask"); } - if(data->set.tls_ech & CURLECH_CLA_CFG && - data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { char *b64val = data->set.str[STRING_ECH_CONFIG]; word32 b64len = 0; b64len = (word32)strlen(b64val); if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len) != WOLFSSL_SUCCESS) { - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -1291,7 +1248,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -1301,7 +1258,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != WOLFSSL_SUCCESS) { infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1312,7 +1269,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1341,9 +1298,6 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, struct ssl_primary_config *conn_config; WOLFSSL_METHOD *req_method = NULL; struct alpn_spec alpns; -#ifdef WOLFSSL_HAVE_KYBER - word16 pqkem = 0; -#endif CURLcode result = CURLE_FAILED_INIT; unsigned char transport; int tls_min, tls_max; @@ -1384,11 +1338,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, if(result) goto out; - result = wssl_init_curves(data, wctx, conn_config, transport -#ifdef WOLFSSL_HAVE_KYBER - , &pqkem -#endif - ); + result = wssl_init_curves(data, wctx, conn_config); if(result) goto out; @@ -1441,7 +1391,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx, data->set.ssl.fsslctxp); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); goto out; } } @@ -1457,17 +1407,13 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #endif result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data, - transport, -#ifdef WOLFSSL_HAVE_KYBER - pqkem, -#endif - sess_reuse_cb); + transport, sess_reuse_cb); if(result) goto out; #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG if(CURLECH_ENABLED(data)) { - result = wssl_init_ech(wctx, cf, data); + result = wssl_init_ech(wctx, cf, data, peer); if(result) goto out; } @@ -1492,9 +1438,9 @@ bool Curl_wssl_need_httpsrr(struct Curl_easy *data) #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG if(!CURLECH_ENABLED(data)) return FALSE; - if((data->set.tls_ech & CURLECH_GREASE) || - (data->set.tls_ech & CURLECH_CLA_CFG)) - return FALSE; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; #else (void)data; @@ -1722,7 +1668,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * store to verify the coming certificate from the server */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); return result; } } @@ -1765,9 +1711,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "unable to get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } - ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.hostname, 0); + ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.origin->hostname, 0); CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", - connssl->peer.hostname, ret); + connssl->peer.origin->hostname, ret); if(ret != WOLFSSL_SUCCESS) detail = DOMAIN_NAME_MISMATCH; wolfSSL_X509_free(cert); @@ -1790,7 +1736,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", - connssl->peer.dispname); + connssl->peer.origin->hostname); return CURLE_PEER_FAILED_VERIFICATION; } else if(ASN_NO_SIGNER_E == detail) { @@ -1915,7 +1861,7 @@ static CURLcode wssl_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } @@ -2175,7 +2121,8 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, /* if we do ECH and need the HTTPS-RR information for it, * we delay the connect until it arrives or DNS resolve fails. */ if(Curl_wssl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } @@ -2211,7 +2158,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, wssl->hs_result = result; goto out; } - /* handhshake was done without errors */ + /* handshake was done without errors */ #ifdef HAVE_ALPN if(connssl->alpn) { int rc; @@ -2285,8 +2232,8 @@ static CURLcode wssl_random(struct Curl_easy *data, return CURLE_OK; } -static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode wssl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { @@ -2294,8 +2241,15 @@ static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ (void)unused; if(wc_InitSha256(&SHA256pw)) return CURLE_FAILED_INIT; - wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); - wc_Sha256Final(&SHA256pw, sha256sum); + do { + word32 ilen = (word32)CURLMIN(len, UINT32_MAX); + if(wc_Sha256Update(&SHA256pw, input, ilen)) + return CURLE_BAD_FUNCTION_ARGUMENT; + len -= ilen; + input += ilen; + } while(len); + if(wc_Sha256Final(&SHA256pw, sha256sum)) + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } diff --git a/deps/curl/lib/vtls/x509asn1.c b/deps/curl/lib/vtls/x509asn1.c index 788dfb278a..4c5dac3e8b 100644 --- a/deps/curl/lib/vtls/x509asn1.c +++ b/deps/curl/lib/vtls/x509asn1.c @@ -1013,7 +1013,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, return 1; /* Compute key length. */ - for(q = elem.beg; !*q && q < elem.end; q++) + for(q = elem.beg; q < elem.end && !*q; q++) ; len = ((elem.end - q) * 8); if(len) { @@ -1062,7 +1062,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, if(p) { if(do_pubkey_field(data, certnum, "dh(p)", &elem)) return 1; - if(getASN1Element(&elem, param->beg, param->end)) { + if(getASN1Element(&elem, p, param->end)) { if(do_pubkey_field(data, certnum, "dh(g)", &elem)) return 1; if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) diff --git a/deps/curl/lib/ws.c b/deps/curl/lib/ws.c index d7840f1ffb..9820c3e4bd 100644 --- a/deps/curl/lib/ws.c +++ b/deps/curl/lib/ws.c @@ -32,6 +32,7 @@ #include "curlx/dynbuf.h" #include "rand.h" #include "curlx/base64.h" +#include "cf-recvbuf.h" #include "connect.h" #include "sendf.h" #include "curl_trc.h" @@ -632,6 +633,7 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, size_t plen, unsigned int frame_type) { + (void)data; DEBUGASSERT(plen <= WS_MAX_CNTRL_LEN); if(plen > WS_MAX_CNTRL_LEN) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -641,13 +643,6 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, ws->pending.type = frame_type; ws->pending.payload_len = plen; memcpy(ws->pending.payload, payload, plen); - - if(!ws->enc.payload_remain) { /* not in the middle of another frame */ - CURLcode result = ws_enc_add_pending(data, ws); - if(!result) - (void)ws_flush(data, ws, Curl_is_in_callback(data)); - return result; - } return CURLE_OK; } @@ -716,7 +711,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, { struct ws_cw_ctx *ctx = writer->ctx; struct websocket *ws; - CURLcode result; + CURLcode result = CURLE_OK; CURL_TRC_WRITE(data, "ws_cw_write(len=%zu, type=%d)", nbytes, type); if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) @@ -733,7 +728,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf, nbytes, &nwritten); if(result) { - infof(data, "[WS] error adding data to buffer %d", result); + infof(data, "[WS] error adding data to buffer %d", (int)result); return result; } } @@ -749,7 +744,8 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if(result == CURLE_AGAIN) { /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ - return CURLE_OK; + result = CURLE_OK; + goto out; } else if(result) { failf(data, "[WS] decode payload error %d", (int)result); @@ -760,10 +756,16 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { failf(data, "[WS] decode ending with %zu frame bytes remaining", Curl_bufq_len(&ctx->buf)); - return CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; } - return CURLE_OK; +out: + if(!result) { + result = ws_flush(data, ws, Curl_is_in_callback(data)); + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; } /* WebSocket payload decoding client writer. */ @@ -995,7 +997,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding head: %d", - result); + (int)result); goto out; } result = ws_enc_write_payload(&ws->enc, data, ws->pending.payload, @@ -1003,7 +1005,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf, &n); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding payload: %d", - result); + (int)result); goto out; } if(n != ws->pending.payload_len) { @@ -1065,7 +1067,8 @@ static CURLcode ws_enc_send(struct Curl_easy *data, fragsize : (curl_off_t)buflen, &ws->sendbuf); if(result) { - CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", result); + CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", + (int)result); return result; } } @@ -1219,7 +1222,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_ws_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1390,15 +1393,17 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, k->header = FALSE; /* we will not get more response headers */ if(data->set.connect_only) { - size_t nwritten; /* In CONNECT_ONLY setup, the payloads from `mem` need to be received - * when using `curl_ws_recv` later on after this transfer is already - * marked as DONE. */ - result = Curl_bufq_write(&ws->recvbuf, (const uint8_t *)mem, - nread, &nwritten); - if(result) - goto out; - DEBUGASSERT(nread == nwritten); + * when using `curl_ws_recv/curl_easy_recv` later on, after this transfer + * is already marked as DONE. + * Since `curl_easy_recv()` is also supposed to work, we need + * to buffer the data at connection level. See #22107 */ + if(nread) { + result = Curl_cf_recvbuf_add(data, data->conn, FIRSTSOCKET, + (const uint8_t *)mem, nread); + if(result) + goto out; + } CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ @@ -1443,7 +1448,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, if(ws_enc_reader) Curl_creader_free(data, ws_enc_reader); if(result) - CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", result); + CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", (int)result); else CURL_TRC_WS(data, "websocket established, %s mode", data->set.connect_only ? "connect-only" : "callback"); @@ -1627,8 +1632,16 @@ CURLcode curl_ws_recv(CURL *curl, void *buffer, static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, bool blocking) { + CURLcode result; + + /* If there is space, add any pending control frame */ + if(Curl_bufq_len(&ws->sendbuf) < ws->sendbuf.chunk_size) { + result = ws_enc_add_pending(data, ws); + if(result && (result != CURLE_AGAIN)) + return result; + } + if(!Curl_bufq_is_empty(&ws->sendbuf)) { - CURLcode result; const uint8_t *out; size_t outlen, n; #ifdef DEBUGBUILD @@ -1670,7 +1683,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return result; } else if(result) { - failf(data, "[WS] flush, write error %d", result); + failf(data, "[WS] flush, write error %d", (int)result); return result; } else { @@ -1761,7 +1774,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, } CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu", - buflen, result, *pnwritten); + buflen, (int)result, *pnwritten); return result; } @@ -1837,7 +1850,7 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, out: CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T ", flags=%x, raw=%d) -> %d, %zu", - buflen, fragsize, flags, data->set.ws_raw_mode, result, + buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, *pnsent); return result; } @@ -1909,7 +1922,7 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, &ws->sendbuf); if(result) CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", - result); + (int)result); out: return result;