summaryrefslogtreecommitdiff
path: root/Utilities
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2023-02-21 10:01:04 -0500
committerBrad King <brad.king@kitware.com>2023-02-21 10:04:36 -0500
commitce1550f1783ba7ca981468621da4bc33d065b508 (patch)
treed66a86859605f70063f161d00fe7f61e5ffb32f5 /Utilities
parent37cceabc6531884045c88c2b461f7e9a7054edd2 (diff)
parent11ba4361aaecf2f1f82ef841146c4c90173d2aca (diff)
downloadcmake-ce1550f1783ba7ca981468621da4bc33d065b508.tar.gz
Merge branch 'upstream-curl' into update-curl
* upstream-curl: curl 2023-02-20 (046209e5)
Diffstat (limited to 'Utilities')
-rw-r--r--Utilities/cmcurl/CMake/CMakeConfigurableFile.in2
-rw-r--r--Utilities/cmcurl/CMake/CurlSymbolHiding.cmake2
-rw-r--r--Utilities/cmcurl/CMake/CurlTests.c2
-rw-r--r--Utilities/cmcurl/CMake/FindBearSSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindBrotli.cmake6
-rw-r--r--Utilities/cmcurl/CMake/FindCARES.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindGSS.cmake6
-rw-r--r--Utilities/cmcurl/CMake/FindLibPSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindLibSSH2.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindMSH3.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindMbedTLS.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGHTTP2.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGHTTP3.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGTCP2.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNSS.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindQUICHE.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindWolfSSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindZstd.cmake2
-rw-r--r--Utilities/cmcurl/CMake/Macros.cmake2
-rw-r--r--Utilities/cmcurl/CMake/OtherTests.cmake2
-rw-r--r--Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake2
-rw-r--r--Utilities/cmcurl/CMake/Utilities.cmake2
-rw-r--r--Utilities/cmcurl/CMake/cmake_uninstall.cmake.in2
-rw-r--r--Utilities/cmcurl/CMake/curl-config.cmake.in2
-rw-r--r--Utilities/cmcurl/CMakeLists.txt34
-rw-r--r--Utilities/cmcurl/COPYING2
-rw-r--r--Utilities/cmcurl/include/curl/curl.h16
-rw-r--r--Utilities/cmcurl/include/curl/curlver.h12
-rw-r--r--Utilities/cmcurl/include/curl/easy.h2
-rw-r--r--Utilities/cmcurl/include/curl/header.h2
-rw-r--r--Utilities/cmcurl/include/curl/mprintf.h2
-rw-r--r--Utilities/cmcurl/include/curl/multi.h2
-rw-r--r--Utilities/cmcurl/include/curl/options.h2
-rw-r--r--Utilities/cmcurl/include/curl/stdcheaders.h2
-rw-r--r--Utilities/cmcurl/include/curl/system.h20
-rw-r--r--Utilities/cmcurl/include/curl/typecheck-gcc.h8
-rw-r--r--Utilities/cmcurl/include/curl/urlapi.h4
-rw-r--r--Utilities/cmcurl/include/curl/websockets.h3
-rw-r--r--Utilities/cmcurl/lib/CMakeLists.txt5
-rw-r--r--Utilities/cmcurl/lib/Makefile.inc24
-rw-r--r--Utilities/cmcurl/lib/altsvc.c2
-rw-r--r--Utilities/cmcurl/lib/altsvc.h2
-rw-r--r--Utilities/cmcurl/lib/amigaos.c2
-rw-r--r--Utilities/cmcurl/lib/amigaos.h2
-rw-r--r--Utilities/cmcurl/lib/arpa_telnet.h2
-rw-r--r--Utilities/cmcurl/lib/asyn-ares.c2
-rw-r--r--Utilities/cmcurl/lib/asyn-thread.c2
-rw-r--r--Utilities/cmcurl/lib/asyn.h2
-rw-r--r--Utilities/cmcurl/lib/base64.c2
-rw-r--r--Utilities/cmcurl/lib/bufref.c2
-rw-r--r--Utilities/cmcurl/lib/bufref.h2
-rw-r--r--Utilities/cmcurl/lib/c-hyper.c24
-rw-r--r--Utilities/cmcurl/lib/c-hyper.h2
-rw-r--r--Utilities/cmcurl/lib/cf-http.c518
-rw-r--r--Utilities/cmcurl/lib/cf-http.h58
-rw-r--r--Utilities/cmcurl/lib/cf-socket.c1908
-rw-r--r--Utilities/cmcurl/lib/cf-socket.h192
-rw-r--r--Utilities/cmcurl/lib/cfilters.c511
-rw-r--r--Utilities/cmcurl/lib/cfilters.h331
-rw-r--r--Utilities/cmcurl/lib/conncache.c4
-rw-r--r--Utilities/cmcurl/lib/conncache.h4
-rw-r--r--Utilities/cmcurl/lib/connect.c2422
-rw-r--r--Utilities/cmcurl/lib/connect.h137
-rw-r--r--Utilities/cmcurl/lib/content_encoding.c46
-rw-r--r--Utilities/cmcurl/lib/content_encoding.h5
-rw-r--r--Utilities/cmcurl/lib/cookie.c24
-rw-r--r--Utilities/cmcurl/lib/cookie.h2
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.c2
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.h2
-rw-r--r--Utilities/cmcurl/lib/curl_base64.h2
-rw-r--r--Utilities/cmcurl/lib/curl_config.h.cmake2
-rw-r--r--Utilities/cmcurl/lib/curl_ctype.h4
-rw-r--r--Utilities/cmcurl/lib/curl_des.c2
-rw-r--r--Utilities/cmcurl/lib/curl_des.h2
-rw-r--r--Utilities/cmcurl/lib/curl_endian.c2
-rw-r--r--Utilities/cmcurl/lib/curl_endian.h2
-rw-r--r--Utilities/cmcurl/lib/curl_fnmatch.c2
-rw-r--r--Utilities/cmcurl/lib/curl_fnmatch.h2
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.c2
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.h2
-rw-r--r--Utilities/cmcurl/lib/curl_gethostname.c2
-rw-r--r--Utilities/cmcurl/lib/curl_gethostname.h2
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.c2
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.h2
-rw-r--r--Utilities/cmcurl/lib/curl_hmac.h2
-rw-r--r--Utilities/cmcurl/lib/curl_krb5.h2
-rw-r--r--Utilities/cmcurl/lib/curl_ldap.h2
-rw-r--r--Utilities/cmcurl/lib/curl_log.c223
-rw-r--r--Utilities/cmcurl/lib/curl_log.h138
-rw-r--r--Utilities/cmcurl/lib/curl_md4.h2
-rw-r--r--Utilities/cmcurl/lib/curl_md5.h2
-rw-r--r--Utilities/cmcurl/lib/curl_memory.h2
-rw-r--r--Utilities/cmcurl/lib/curl_memrchr.c2
-rw-r--r--Utilities/cmcurl/lib/curl_memrchr.h2
-rw-r--r--Utilities/cmcurl/lib/curl_multibyte.c2
-rw-r--r--Utilities/cmcurl/lib/curl_multibyte.h2
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.c15
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.h8
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.c2
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.h2
-rw-r--r--Utilities/cmcurl/lib/curl_path.c2
-rw-r--r--Utilities/cmcurl/lib/curl_path.h2
-rw-r--r--Utilities/cmcurl/lib/curl_printf.h2
-rw-r--r--Utilities/cmcurl/lib/curl_range.c2
-rw-r--r--Utilities/cmcurl/lib/curl_range.h2
-rw-r--r--Utilities/cmcurl/lib/curl_rtmp.c4
-rw-r--r--Utilities/cmcurl/lib/curl_rtmp.h2
-rw-r--r--Utilities/cmcurl/lib/curl_sasl.c5
-rw-r--r--Utilities/cmcurl/lib/curl_sasl.h8
-rw-r--r--Utilities/cmcurl/lib/curl_setup.h27
-rw-r--r--Utilities/cmcurl/lib/curl_setup_once.h2
-rw-r--r--Utilities/cmcurl/lib/curl_sha256.h4
-rw-r--r--Utilities/cmcurl/lib/curl_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/curl_sspi.h2
-rw-r--r--Utilities/cmcurl/lib/curl_threads.c2
-rw-r--r--Utilities/cmcurl/lib/curl_threads.h2
-rw-r--r--Utilities/cmcurl/lib/curlx.h2
-rw-r--r--Utilities/cmcurl/lib/dict.c88
-rw-r--r--Utilities/cmcurl/lib/dict.h2
-rw-r--r--Utilities/cmcurl/lib/doh.c4
-rw-r--r--Utilities/cmcurl/lib/doh.h2
-rw-r--r--Utilities/cmcurl/lib/dynbuf.c2
-rw-r--r--Utilities/cmcurl/lib/dynbuf.h2
-rw-r--r--Utilities/cmcurl/lib/easy.c49
-rw-r--r--Utilities/cmcurl/lib/easy_lock.h2
-rw-r--r--Utilities/cmcurl/lib/easygetopt.c2
-rw-r--r--Utilities/cmcurl/lib/easyif.h2
-rw-r--r--Utilities/cmcurl/lib/easyoptions.c6
-rw-r--r--Utilities/cmcurl/lib/easyoptions.h2
-rw-r--r--Utilities/cmcurl/lib/escape.c62
-rw-r--r--Utilities/cmcurl/lib/escape.h2
-rw-r--r--Utilities/cmcurl/lib/file.c2
-rw-r--r--Utilities/cmcurl/lib/file.h2
-rw-r--r--Utilities/cmcurl/lib/fileinfo.c2
-rw-r--r--Utilities/cmcurl/lib/fileinfo.h2
-rw-r--r--Utilities/cmcurl/lib/fopen.c3
-rw-r--r--Utilities/cmcurl/lib/fopen.h2
-rw-r--r--Utilities/cmcurl/lib/formdata.c2
-rw-r--r--Utilities/cmcurl/lib/formdata.h2
-rw-r--r--Utilities/cmcurl/lib/ftp.c49
-rw-r--r--Utilities/cmcurl/lib/ftp.h51
-rw-r--r--Utilities/cmcurl/lib/ftplistparser.c2
-rw-r--r--Utilities/cmcurl/lib/ftplistparser.h2
-rw-r--r--Utilities/cmcurl/lib/functypes.h2
-rw-r--r--Utilities/cmcurl/lib/getenv.c2
-rw-r--r--Utilities/cmcurl/lib/getinfo.c2
-rw-r--r--Utilities/cmcurl/lib/getinfo.h2
-rw-r--r--Utilities/cmcurl/lib/gopher.c2
-rw-r--r--Utilities/cmcurl/lib/gopher.h2
-rw-r--r--Utilities/cmcurl/lib/h2h3.c9
-rw-r--r--Utilities/cmcurl/lib/h2h3.h3
-rw-r--r--Utilities/cmcurl/lib/hash.c2
-rw-r--r--Utilities/cmcurl/lib/hash.h2
-rw-r--r--Utilities/cmcurl/lib/headers.c2
-rw-r--r--Utilities/cmcurl/lib/headers.h2
-rw-r--r--Utilities/cmcurl/lib/hmac.c2
-rw-r--r--Utilities/cmcurl/lib/hostasyn.c2
-rw-r--r--Utilities/cmcurl/lib/hostip.c2
-rw-r--r--Utilities/cmcurl/lib/hostip.h2
-rw-r--r--Utilities/cmcurl/lib/hostip4.c2
-rw-r--r--Utilities/cmcurl/lib/hostip6.c2
-rw-r--r--Utilities/cmcurl/lib/hostsyn.c2
-rw-r--r--Utilities/cmcurl/lib/hsts.c30
-rw-r--r--Utilities/cmcurl/lib/hsts.h4
-rw-r--r--Utilities/cmcurl/lib/http.c194
-rw-r--r--Utilities/cmcurl/lib/http.h111
-rw-r--r--Utilities/cmcurl/lib/http2.c2175
-rw-r--r--Utilities/cmcurl/lib/http2.h61
-rw-r--r--Utilities/cmcurl/lib/http_aws_sigv4.c6
-rw-r--r--Utilities/cmcurl/lib/http_aws_sigv4.h2
-rw-r--r--Utilities/cmcurl/lib/http_chunks.c2
-rw-r--r--Utilities/cmcurl/lib/http_chunks.h2
-rw-r--r--Utilities/cmcurl/lib/http_digest.c2
-rw-r--r--Utilities/cmcurl/lib/http_digest.h2
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.c2
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.h2
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.c2
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.h2
-rw-r--r--Utilities/cmcurl/lib/http_proxy.c338
-rw-r--r--Utilities/cmcurl/lib/http_proxy.h19
-rw-r--r--Utilities/cmcurl/lib/idn.c32
-rw-r--r--Utilities/cmcurl/lib/idn.h10
-rw-r--r--Utilities/cmcurl/lib/if2ip.c2
-rw-r--r--Utilities/cmcurl/lib/if2ip.h2
-rw-r--r--Utilities/cmcurl/lib/imap.c18
-rw-r--r--Utilities/cmcurl/lib/imap.h24
-rw-r--r--Utilities/cmcurl/lib/inet_ntop.h2
-rw-r--r--Utilities/cmcurl/lib/inet_pton.c2
-rw-r--r--Utilities/cmcurl/lib/inet_pton.h2
-rw-r--r--Utilities/cmcurl/lib/krb5.c33
-rw-r--r--Utilities/cmcurl/lib/ldap.c2
-rw-r--r--Utilities/cmcurl/lib/libcurl.rc2
-rw-r--r--Utilities/cmcurl/lib/llist.c2
-rw-r--r--Utilities/cmcurl/lib/llist.h2
-rw-r--r--Utilities/cmcurl/lib/md4.c12
-rw-r--r--Utilities/cmcurl/lib/md5.c2
-rw-r--r--Utilities/cmcurl/lib/memdebug.c2
-rw-r--r--Utilities/cmcurl/lib/memdebug.h2
-rw-r--r--Utilities/cmcurl/lib/mime.c2
-rw-r--r--Utilities/cmcurl/lib/mime.h2
-rw-r--r--Utilities/cmcurl/lib/mprintf.c2
-rw-r--r--Utilities/cmcurl/lib/mqtt.c4
-rw-r--r--Utilities/cmcurl/lib/mqtt.h2
-rw-r--r--Utilities/cmcurl/lib/multi.c52
-rw-r--r--Utilities/cmcurl/lib/multihandle.h15
-rw-r--r--Utilities/cmcurl/lib/multiif.h2
-rw-r--r--Utilities/cmcurl/lib/netrc.c2
-rw-r--r--Utilities/cmcurl/lib/netrc.h2
-rw-r--r--Utilities/cmcurl/lib/nonblock.c2
-rw-r--r--Utilities/cmcurl/lib/nonblock.h2
-rw-r--r--Utilities/cmcurl/lib/noproxy.c15
-rw-r--r--Utilities/cmcurl/lib/noproxy.h5
-rw-r--r--Utilities/cmcurl/lib/openldap.c4
-rw-r--r--Utilities/cmcurl/lib/parsedate.c2
-rw-r--r--Utilities/cmcurl/lib/parsedate.h2
-rw-r--r--Utilities/cmcurl/lib/pingpong.c2
-rw-r--r--Utilities/cmcurl/lib/pingpong.h2
-rw-r--r--Utilities/cmcurl/lib/pop3.c14
-rw-r--r--Utilities/cmcurl/lib/pop3.h12
-rw-r--r--Utilities/cmcurl/lib/progress.c38
-rw-r--r--Utilities/cmcurl/lib/progress.h9
-rw-r--r--Utilities/cmcurl/lib/psl.c2
-rw-r--r--Utilities/cmcurl/lib/psl.h2
-rw-r--r--Utilities/cmcurl/lib/quic.h68
-rw-r--r--Utilities/cmcurl/lib/rand.c2
-rw-r--r--Utilities/cmcurl/lib/rand.h2
-rw-r--r--Utilities/cmcurl/lib/rename.c2
-rw-r--r--Utilities/cmcurl/lib/rename.h2
-rw-r--r--Utilities/cmcurl/lib/rtsp.c37
-rw-r--r--Utilities/cmcurl/lib/rtsp.h2
-rw-r--r--Utilities/cmcurl/lib/select.c2
-rw-r--r--Utilities/cmcurl/lib/select.h2
-rw-r--r--Utilities/cmcurl/lib/sendf.c359
-rw-r--r--Utilities/cmcurl/lib/sendf.h47
-rw-r--r--Utilities/cmcurl/lib/setopt.c245
-rw-r--r--Utilities/cmcurl/lib/setopt.h2
-rw-r--r--Utilities/cmcurl/lib/setup-os400.h4
-rw-r--r--Utilities/cmcurl/lib/setup-vms.h2
-rw-r--r--Utilities/cmcurl/lib/setup-win32.h2
-rw-r--r--Utilities/cmcurl/lib/sha256.c4
-rw-r--r--Utilities/cmcurl/lib/share.c34
-rw-r--r--Utilities/cmcurl/lib/share.h8
-rw-r--r--Utilities/cmcurl/lib/sigpipe.h2
-rw-r--r--Utilities/cmcurl/lib/slist.c2
-rw-r--r--Utilities/cmcurl/lib/slist.h2
-rw-r--r--Utilities/cmcurl/lib/smb.c10
-rw-r--r--Utilities/cmcurl/lib/smb.h4
-rw-r--r--Utilities/cmcurl/lib/smtp.c16
-rw-r--r--Utilities/cmcurl/lib/smtp.h20
-rw-r--r--Utilities/cmcurl/lib/sockaddr.h2
-rw-r--r--Utilities/cmcurl/lib/socketpair.c80
-rw-r--r--Utilities/cmcurl/lib/socketpair.h2
-rw-r--r--Utilities/cmcurl/lib/socks.c322
-rw-r--r--Utilities/cmcurl/lib/socks.h13
-rw-r--r--Utilities/cmcurl/lib/socks_gssapi.c48
-rw-r--r--Utilities/cmcurl/lib/socks_sspi.c35
-rw-r--r--Utilities/cmcurl/lib/speedcheck.c2
-rw-r--r--Utilities/cmcurl/lib/speedcheck.h2
-rw-r--r--Utilities/cmcurl/lib/splay.c2
-rw-r--r--Utilities/cmcurl/lib/splay.h2
-rw-r--r--Utilities/cmcurl/lib/strcase.c2
-rw-r--r--Utilities/cmcurl/lib/strcase.h2
-rw-r--r--Utilities/cmcurl/lib/strdup.c4
-rw-r--r--Utilities/cmcurl/lib/strdup.h4
-rw-r--r--Utilities/cmcurl/lib/strerror.c5
-rw-r--r--Utilities/cmcurl/lib/strerror.h2
-rw-r--r--Utilities/cmcurl/lib/strtok.c2
-rw-r--r--Utilities/cmcurl/lib/strtok.h2
-rw-r--r--Utilities/cmcurl/lib/strtoofft.c2
-rw-r--r--Utilities/cmcurl/lib/strtoofft.h2
-rw-r--r--Utilities/cmcurl/lib/system_win32.c2
-rw-r--r--Utilities/cmcurl/lib/system_win32.h2
-rw-r--r--Utilities/cmcurl/lib/telnet.c2
-rw-r--r--Utilities/cmcurl/lib/telnet.h2
-rw-r--r--Utilities/cmcurl/lib/tftp.c11
-rw-r--r--Utilities/cmcurl/lib/tftp.h2
-rw-r--r--Utilities/cmcurl/lib/timediff.c2
-rw-r--r--Utilities/cmcurl/lib/timediff.h2
-rw-r--r--Utilities/cmcurl/lib/timeval.c2
-rw-r--r--Utilities/cmcurl/lib/timeval.h2
-rw-r--r--Utilities/cmcurl/lib/transfer.c114
-rw-r--r--Utilities/cmcurl/lib/transfer.h2
-rw-r--r--Utilities/cmcurl/lib/url.c262
-rw-r--r--Utilities/cmcurl/lib/url.h18
-rw-r--r--Utilities/cmcurl/lib/urlapi-int.h2
-rw-r--r--Utilities/cmcurl/lib/urlapi.c127
-rw-r--r--Utilities/cmcurl/lib/urldata.h189
-rw-r--r--Utilities/cmcurl/lib/vauth/cleartext.c5
-rw-r--r--Utilities/cmcurl/lib/vauth/cram.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest.h2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest_sspi.c4
-rw-r--r--Utilities/cmcurl/lib/vauth/gsasl.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_gssapi.c4
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm.h2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/oauth2.c5
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_gssapi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.h2
-rw-r--r--Utilities/cmcurl/lib/version.c8
-rw-r--r--Utilities/cmcurl/lib/version_win32.c2
-rw-r--r--Utilities/cmcurl/lib/version_win32.h2
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_msh3.c841
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_msh3.h (renamed from Utilities/cmcurl/lib/vquic/msh3.h)22
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_ngtcp2.c2515
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_ngtcp2.h61
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_quiche.c1433
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_quiche.h (renamed from Utilities/cmcurl/lib/vquic/quiche.h)42
-rw-r--r--Utilities/cmcurl/lib/vquic/msh3.c527
-rw-r--r--Utilities/cmcurl/lib/vquic/ngtcp2.c2266
-rw-r--r--Utilities/cmcurl/lib/vquic/quiche.c892
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic.c316
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic.h32
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic_int.h (renamed from Utilities/cmcurl/lib/vquic/ngtcp2.h)81
-rw-r--r--Utilities/cmcurl/lib/vssh/libssh.c2
-rw-r--r--Utilities/cmcurl/lib/vssh/libssh2.c19
-rw-r--r--Utilities/cmcurl/lib/vssh/ssh.h2
-rw-r--r--Utilities/cmcurl/lib/vssh/wolfssh.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/bearssl.c59
-rw-r--r--Utilities/cmcurl/lib/vtls/bearssl.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/gskit.c14
-rw-r--r--Utilities/cmcurl/lib/vtls/gskit.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/gtls.c85
-rw-r--r--Utilities/cmcurl/lib/vtls/gtls.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/hostcheck.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/hostcheck.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/keylog.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/keylog.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls.c64
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c4
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/nss.c70
-rw-r--r--Utilities/cmcurl/lib/vtls/nssg.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.c795
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.h11
-rw-r--r--Utilities/cmcurl/lib/vtls/rustls.c105
-rw-r--r--Utilities/cmcurl/lib/vtls/rustls.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.c67
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.h8
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel_verify.c6
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.c94
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.c478
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.h59
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls_int.h18
-rw-r--r--Utilities/cmcurl/lib/vtls/wolfssl.c77
-rw-r--r--Utilities/cmcurl/lib/vtls/wolfssl.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/x509asn1.c25
-rw-r--r--Utilities/cmcurl/lib/vtls/x509asn1.h2
-rw-r--r--Utilities/cmcurl/lib/warnless.c2
-rw-r--r--Utilities/cmcurl/lib/warnless.h2
-rw-r--r--Utilities/cmcurl/lib/wildcard.c2
-rw-r--r--Utilities/cmcurl/lib/wildcard.h2
-rw-r--r--Utilities/cmcurl/lib/ws.c142
-rw-r--r--Utilities/cmcurl/lib/ws.h18
360 files changed, 14331 insertions, 9802 deletions
diff --git a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
index b93e7539a1..a3d2bc4a28 100644
--- a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
+++ b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
index 75215a122f..142e919449 100644
--- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
+++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 6a9fdea098..3dbba3cbb2 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindBearSSL.cmake b/Utilities/cmcurl/CMake/FindBearSSL.cmake
index 88d5e87ade..56a064eacd 100644
--- a/Utilities/cmcurl/CMake/FindBearSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindBearSSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake
index 833e1811ae..11ab7f8254 100644
--- a/Utilities/cmcurl/CMake/FindBrotli.cmake
+++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -28,7 +28,7 @@ find_path(BROTLI_INCLUDE_DIR "brotli/decode.h")
find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon)
find_library(BROTLIDEC_LIBRARY NAMES brotlidec)
-find_package_handle_standard_args(BROTLI
+find_package_handle_standard_args(Brotli
FOUND_VAR
BROTLI_FOUND
REQUIRED_VARS
@@ -36,7 +36,7 @@ find_package_handle_standard_args(BROTLI
BROTLICOMMON_LIBRARY
BROTLI_INCLUDE_DIR
FAIL_MESSAGE
- "Could NOT find BROTLI"
+ "Could NOT find Brotli"
)
set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
diff --git a/Utilities/cmcurl/CMake/FindCARES.cmake b/Utilities/cmcurl/CMake/FindCARES.cmake
index 99cf31d913..fa75891189 100644
--- a/Utilities/cmcurl/CMake/FindCARES.cmake
+++ b/Utilities/cmcurl/CMake/FindCARES.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake
index ec2bd57bae..b244e610ec 100644
--- a/Utilities/cmcurl/CMake/FindGSS.cmake
+++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -181,14 +181,14 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
set(GSS_FLAVOUR "MIT")
else()
# prevent compiling the header - just check if we can include it
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D__ROKEN_H__")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__)
check_include_file( "roken.h" _GSS_HAVE_ROKEN_H)
check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H)
if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H)
set(GSS_FLAVOUR "Heimdal")
endif()
- set(CMAKE_REQUIRED_DEFINITIONS "")
+ list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__)
endif()
else()
# I'm not convinced if this is the right way but this is what autotools do at the moment
diff --git a/Utilities/cmcurl/CMake/FindLibPSL.cmake b/Utilities/cmcurl/CMake/FindLibPSL.cmake
index 66abdd79a3..e3bd68d1d7 100644
--- a/Utilities/cmcurl/CMake/FindLibPSL.cmake
+++ b/Utilities/cmcurl/CMake/FindLibPSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindLibSSH2.cmake b/Utilities/cmcurl/CMake/FindLibSSH2.cmake
index 0ec7f7e3b9..a0c251ae39 100644
--- a/Utilities/cmcurl/CMake/FindLibSSH2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibSSH2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindMSH3.cmake b/Utilities/cmcurl/CMake/FindMSH3.cmake
index 96477e2828..7d9c6b6544 100644
--- a/Utilities/cmcurl/CMake/FindMSH3.cmake
+++ b/Utilities/cmcurl/CMake/FindMSH3.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
index fcd6717f6f..814bd97da1 100644
--- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake
+++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
index 6d70c4a049..3957646c43 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
index 8d8ebc1b1e..9b13e6c6ff 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
index 61e54c2d67..9f4e9f233f 100644
--- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake
index 6742dda83f..ccddf422a5 100644
--- a/Utilities/cmcurl/CMake/FindNSS.cmake
+++ b/Utilities/cmcurl/CMake/FindNSS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindQUICHE.cmake b/Utilities/cmcurl/CMake/FindQUICHE.cmake
index fc47027d74..0488463d55 100644
--- a/Utilities/cmcurl/CMake/FindQUICHE.cmake
+++ b/Utilities/cmcurl/CMake/FindQUICHE.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
index 986f01e00e..d67c0eb24d 100644
--- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake
index 2d6540443c..973e6ad4a9 100644
--- a/Utilities/cmcurl/CMake/FindZstd.cmake
+++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake
index 4d7380eb5f..e12bf303f4 100644
--- a/Utilities/cmcurl/CMake/Macros.cmake
+++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index ed8d28a216..fa1e458e61 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
index 3cb4ffec3d..cef31b55fa 100644
--- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
+++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake
index 78bfd6ffe6..9ff38e3a0f 100644
--- a/Utilities/cmcurl/CMake/Utilities.cmake
+++ b/Utilities/cmcurl/CMake/Utilities.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
index 2549d41f42..3e0742d1e4 100644
--- a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
+++ b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in
index 496a92d0e5..dbe4ed2102 100644
--- a/Utilities/cmcurl/CMake/curl-config.cmake.in
+++ b/Utilities/cmcurl/CMake/curl-config.cmake.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 49016c8863..6fc316dda3 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -150,7 +150,7 @@ endif()
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -253,7 +253,7 @@ if(WIN32)
set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string")
if(CURL_TARGET_WINDOWS_VERSION)
add_definitions(-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION})
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION})
set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
endif()
endif()
@@ -488,7 +488,7 @@ include(CheckCSourceCompiles)
# On windows preload settings
if(WIN32)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WINSOCKAPI_=")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake)
endif()
@@ -514,24 +514,6 @@ if(WIN32)
endif()
if(0) # This code not needed for building within CMake.
-# This check below for use of deprecated symbols is only temporary and is to
-# be removed again after a year's service. Remove after November 25, 2022.
-set(CURL_RECONFIG_REQUIRED 0)
-foreach(_LIB GSSAPI OPENLDAP LIBSSH LIBSSH2 BEARSSL MBEDTLS NSS OPENSSL
- SCHANNEL SECTRANSP WOLFSSL)
- if(CMAKE_USE_${_LIB})
- set(CURL_RECONFIG_REQUIRED 1)
- message(SEND_ERROR "The option CMAKE_USE_${_LIB} was renamed to CURL_USE_${_LIB}.")
- endif()
-endforeach()
-if(CMAKE_USE_WINSSL)
- set(CURL_RECONFIG_REQUIRED 1)
- message(SEND_ERROR "The option CMAKE_USE_WINSSL was renamed to CURL_USE_SCHANNEL.")
-endif()
-if(CURL_RECONFIG_REQUIRED)
- message(FATAL_ERROR "Reconfig required")
-endif()
-
# check SSL libraries
# TODO support GnuTLS
option(CURL_ENABLE_SSL "Enable SSL support" ON)
@@ -577,7 +559,6 @@ if(CURL_USE_SCHANNEL)
endif()
if(CURL_WINDOWS_SSPI)
set(USE_WINDOWS_SSPI ON)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DSECURITY_WIN32")
endif()
if(CURL_USE_SECTRANSP)
@@ -630,8 +611,6 @@ if(CURL_USE_OPENSSL)
if(CURL_CA_PATH)
add_definitions(-DCURL_CA_PATH="${CURL_CA_PATH}")
endif()
-
- add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED)
endif()
if(CURL_USE_MBEDTLS)
@@ -826,7 +805,7 @@ if(NOT CURL_DISABLE_LDAP)
return 0;
}"
)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DLDAP_DEPRECATED=1")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
if(HAVE_LIBLBER)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
@@ -1204,6 +1183,7 @@ check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET)
check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
check_symbol_exists(recv "${CURL_INCLUDES}" HAVE_RECV)
check_symbol_exists(send "${CURL_INCLUDES}" HAVE_SEND)
+check_symbol_exists(sendmsg "${CURL_INCLUDES}" HAVE_SENDMSG)
check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT)
check_symbol_exists(strdup "${CURL_INCLUDES}" HAVE_STRDUP)
check_symbol_exists(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R)
@@ -1248,7 +1228,7 @@ check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT)
if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
# earlier MSVC compilers had faulty snprintf implementations
- check_symbol_exists(snprintf "${CURL_INCLUDES}" HAVE_SNPRINTF)
+ check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
endif()
check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME)
check_symbol_exists(inet_ntop "${CURL_INCLUDES}" HAVE_INET_NTOP)
@@ -1451,7 +1431,7 @@ if(WIN32)
# Check if crypto functions in wincrypt.h are actually available
if(HAVE_WINCRYPT_H)
- check_symbol_exists(CryptAcquireContext "${CURL_INCLUDES}" USE_WINCRYPT)
+ check_symbol_exists(CryptAcquireContext "windows.h;wincrypt.h" USE_WINCRYPT)
endif()
if(USE_WINCRYPT)
set(USE_WIN32_CRYPTO ON)
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING
index 90f05adf25..d1eab3eb93 100644
--- a/Utilities/cmcurl/COPYING
+++ b/Utilities/cmcurl/COPYING
@@ -1,6 +1,6 @@
COPYRIGHT AND PERMISSION NOTICE
-Copyright (c) 1996 - 2022, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index b354757afc..c5ce0a59b5 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -167,7 +167,7 @@ typedef enum {
CURLSSLBACKEND_SECURETRANSPORT = 9,
CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10,
CURLSSLBACKEND_MBEDTLS = 11,
- CURLSSLBACKEND_MESALINK = 12,
+ CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12,
CURLSSLBACKEND_BEARSSL = 13,
CURLSSLBACKEND_RUSTLS = 14
} curl_sslbackend;
@@ -248,7 +248,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 524288
+#define CURL_MAX_READ_SIZE (10*1024*1024)
#endif
#ifndef CURL_MAX_WRITE_SIZE
@@ -2259,8 +2259,13 @@ enum {
CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1
Upgrade */
- CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback.
- Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */
+ CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if
+ needed. For HTTPS only. For HTTP, this option
+ makes libcurl return error. */
+ CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS
+ only. For HTTP, this makes libcurl
+ return error. */
+
CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
};
@@ -2953,6 +2958,7 @@ typedef enum {
CURL_LOCK_DATA_SSL_SESSION,
CURL_LOCK_DATA_CONNECT,
CURL_LOCK_DATA_PSL,
+ CURL_LOCK_DATA_HSTS,
CURL_LOCK_DATA_LAST
} curl_lock_data;
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 94db4cde00..ce39cdc5b8 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,17 +28,17 @@
a script at release-time. This was made its own header file in 7.11.2 */
/* This is the global package copyright */
-#define LIBCURL_COPYRIGHT "1996 - 2022 Daniel Stenberg, <daniel@haxx.se>."
+#define LIBCURL_COPYRIGHT "Daniel Stenberg, <daniel@haxx.se>."
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "7.87.0"
+#define LIBCURL_VERSION "7.88.1"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 87
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_MINOR 88
+#define LIBCURL_VERSION_PATCH 1
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
-#define LIBCURL_VERSION_NUM 0x075700
+#define LIBCURL_VERSION_NUM 0x075801
/*
* This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h
index 98ee8888ed..394668a8fa 100644
--- a/Utilities/cmcurl/include/curl/easy.h
+++ b/Utilities/cmcurl/include/curl/easy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h
index 1598c6f113..8df11e1e42 100644
--- a/Utilities/cmcurl/include/curl/header.h
+++ b/Utilities/cmcurl/include/curl/header.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h
index 06ef5c6d8c..e652a6520e 100644
--- a/Utilities/cmcurl/include/curl/mprintf.h
+++ b/Utilities/cmcurl/include/curl/mprintf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h
index c956d28e8f..30a3d93017 100644
--- a/Utilities/cmcurl/include/curl/multi.h
+++ b/Utilities/cmcurl/include/curl/multi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/options.h b/Utilities/cmcurl/include/curl/options.h
index a792687cf4..1ed76a95c6 100644
--- a/Utilities/cmcurl/include/curl/options.h
+++ b/Utilities/cmcurl/include/curl/options.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/stdcheaders.h b/Utilities/cmcurl/include/curl/stdcheaders.h
index 82e1b5fef6..7451aa3052 100644
--- a/Utilities/cmcurl/include/curl/stdcheaders.h
+++ b/Utilities/cmcurl/include/curl/stdcheaders.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h
index 11db51ecf3..def7739242 100644
--- a/Utilities/cmcurl/include/curl/system.h
+++ b/Utilities/cmcurl/include/curl/system.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -227,16 +227,14 @@
# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
#elif defined(__OS400__)
-# if defined(__ILEC400__)
-# define CURL_TYPEOF_CURL_OFF_T long long
-# define CURL_FORMAT_CURL_OFF_T "lld"
-# define CURL_FORMAT_CURL_OFF_TU "llu"
-# define CURL_SUFFIX_CURL_OFF_T LL
-# define CURL_SUFFIX_CURL_OFF_TU ULL
-# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
-# define CURL_PULL_SYS_TYPES_H 1
-# define CURL_PULL_SYS_SOCKET_H 1
-# endif
+# define CURL_TYPEOF_CURL_OFF_T long long
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+# define CURL_SUFFIX_CURL_OFF_T LL
+# define CURL_SUFFIX_CURL_OFF_TU ULL
+# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+# define CURL_PULL_SYS_TYPES_H 1
+# define CURL_PULL_SYS_SOCKET_H 1
#elif defined(__MVS__)
# if defined(__IBMC__) || defined(__IBMCPP__)
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h
index bf655bb632..bc8d7a78ce 100644
--- a/Utilities/cmcurl/include/curl/typecheck-gcc.h
+++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,9 +42,8 @@
*/
#define curl_easy_setopt(handle, option, value) \
__extension__({ \
- CURL_IGNORE_DEPRECATION(__typeof__(option) _curl_opt = option;) \
+ CURLoption _curl_opt = (option); \
if(__builtin_constant_p(_curl_opt)) { \
- (void) option; \
CURL_IGNORE_DEPRECATION( \
if(curlcheck_long_option(_curl_opt)) \
if(!curlcheck_long(value)) \
@@ -120,9 +119,8 @@
/* wraps curl_easy_getinfo() with typechecking */
#define curl_easy_getinfo(handle, info, arg) \
__extension__({ \
- CURL_IGNORE_DEPRECATION(__typeof__(info) _curl_info = info;) \
+ CURLINFO _curl_info = (info); \
if(__builtin_constant_p(_curl_info)) { \
- (void) info; \
CURL_IGNORE_DEPRECATION( \
if(curlcheck_string_info(_curl_info)) \
if(!curlcheck_arr((arg), char *)) \
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index e15c213ccd..b97b53475a 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,6 +62,7 @@ typedef enum {
CURLUE_BAD_SCHEME, /* 27 */
CURLUE_BAD_SLASHES, /* 28 */
CURLUE_BAD_USER, /* 29 */
+ CURLUE_LACKS_IDN, /* 30 */
CURLUE_LAST
} CURLUcode;
@@ -95,6 +96,7 @@ typedef enum {
#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the
scheme is unknown. */
#define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */
+#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */
typedef struct Curl_URL CURLU;
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h
index 4d57f91e56..fd6a916547 100644
--- a/Utilities/cmcurl/include/curl/websockets.h
+++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,6 +33,7 @@ struct curl_ws_frame {
int flags; /* See the CURLWS_* defines */
curl_off_t offset; /* the offset of this data into the frame */
curl_off_t bytesleft; /* number of pending bytes left of the payload */
+ size_t len; /* size of the current data chunk */
};
/* flag bits */
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index 074ab9dac7..15853ff72b 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -136,11 +136,12 @@ set_target_properties(${LIB_NAME} PROPERTIES
if(0) # This code not needed for building within CMake.
if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+ CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
# FreeBSD comes with the a.out and elf flavours
# but a.out was supported up to version 3.x and
- # elf from 3.x. I cannot imagine someone runnig
+ # elf from 3.x. I cannot imagine someone running
# CMake on those ancient systems
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index 9eafa93b9a..c28b4756b0 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -79,16 +79,17 @@ LIB_VTLS_HFILES = \
vtls/x509asn1.h
LIB_VQUIC_CFILES = \
- vquic/msh3.c \
- vquic/ngtcp2.c \
- vquic/quiche.c \
+ vquic/curl_msh3.c \
+ vquic/curl_ngtcp2.c \
+ vquic/curl_quiche.c \
vquic/vquic.c
LIB_VQUIC_HFILES = \
- vquic/msh3.h \
- vquic/ngtcp2.h \
- vquic/quiche.h \
- vquic/vquic.h
+ vquic/curl_msh3.h \
+ vquic/curl_ngtcp2.h \
+ vquic/curl_quiche.h \
+ vquic/vquic.h \
+ vquic/vquic_int.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
@@ -106,6 +107,8 @@ LIB_CFILES = \
base64.c \
bufref.c \
c-hyper.c \
+ cf-http.c \
+ cf-socket.c \
cfilters.c \
conncache.c \
connect.c \
@@ -118,6 +121,7 @@ LIB_CFILES = \
curl_get_line.c \
curl_gethostname.c \
curl_gssapi.c \
+ curl_log.c \
curl_memrchr.c \
curl_multibyte.c \
curl_ntlm_core.c \
@@ -229,6 +233,8 @@ LIB_HFILES = \
asyn.h \
bufref.h \
c-hyper.h \
+ cf-http.h \
+ cf-socket.h \
cfilters.h \
conncache.h \
connect.h \
@@ -246,6 +252,7 @@ LIB_HFILES = \
curl_hmac.h \
curl_krb5.h \
curl_ldap.h \
+ curl_log.h \
curl_md4.h \
curl_md5.h \
curl_memory.h \
@@ -312,7 +319,6 @@ LIB_HFILES = \
pop3.h \
progress.h \
psl.h \
- quic.h \
rand.h \
rename.h \
rtsp.h \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index ec18e381c8..31a7abcc5d 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h
index 2751d272a1..7fea1434a5 100644
--- a/Utilities/cmcurl/lib/altsvc.h
+++ b/Utilities/cmcurl/lib/altsvc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c
index e8c2fc02e0..b0a9500267 100644
--- a/Utilities/cmcurl/lib/amigaos.c
+++ b/Utilities/cmcurl/lib/amigaos.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h
index 9abfb59eac..7997ede80e 100644
--- a/Utilities/cmcurl/lib/amigaos.h
+++ b/Utilities/cmcurl/lib/amigaos.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h
index 523f7f51e7..de1373800c 100644
--- a/Utilities/cmcurl/lib/arpa_telnet.h
+++ b/Utilities/cmcurl/lib/arpa_telnet.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 4436da3c4d..19fe8536b0 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index 705f0f612a..4d7f8605f7 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h
index 1aab21aaa7..7e207c4f56 100644
--- a/Utilities/cmcurl/lib/asyn.h
+++ b/Utilities/cmcurl/lib/asyn.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c
index bacd627cf4..e1b7b7287a 100644
--- a/Utilities/cmcurl/lib/base64.c
+++ b/Utilities/cmcurl/lib/base64.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/bufref.c b/Utilities/cmcurl/lib/bufref.c
index 91b037431d..ce686b6f37 100644
--- a/Utilities/cmcurl/lib/bufref.c
+++ b/Utilities/cmcurl/lib/bufref.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/bufref.h b/Utilities/cmcurl/lib/bufref.h
index 96b818b537..dd424f18f5 100644
--- a/Utilities/cmcurl/lib/bufref.h
+++ b/Utilities/cmcurl/lib/bufref.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c
index 65f5581aee..9c7632d354 100644
--- a/Utilities/cmcurl/lib/c-hyper.c
+++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -485,7 +485,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
if(k->upgr101 == UPGR101_WS) {
if(http_status == 101) {
/* verify the response */
- result = Curl_ws_accept(data);
+ result = Curl_ws_accept(data, NULL, 0);
if(result)
return result;
}
@@ -1128,6 +1128,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
goto error;
}
+#ifdef HAVE_LIBZ
+ /* we only consider transfer-encoding magic if libz support is built-in */
+ result = Curl_transferencode(data);
+ if(result)
+ goto error;
+ result = Curl_hyper_header(data, headers, data->state.aptr.te);
+ if(result)
+ goto error;
+#endif
+
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(data->state.aptr.accept_encoding);
@@ -1144,16 +1154,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
else
Curl_safefree(data->state.aptr.accept_encoding);
-#ifdef HAVE_LIBZ
- /* we only consider transfer-encoding magic if libz support is built-in */
- result = Curl_transferencode(data);
- if(result)
- goto error;
- result = Curl_hyper_header(data, headers, data->state.aptr.te);
- if(result)
- goto error;
-#endif
-
result = cookies(data, conn, headers);
if(result)
goto error;
diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h
index 70507ad2a4..4218cda75f 100644
--- a/Utilities/cmcurl/lib/c-hyper.h
+++ b/Utilities/cmcurl/lib/c-hyper.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/cf-http.c b/Utilities/cmcurl/lib/cf-http.c
new file mode 100644
index 0000000000..2ee3d4da60
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-http.c
@@ -0,0 +1,518 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "curl_log.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "multiif.h"
+#include "cf-http.h"
+#include "http2.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+typedef enum {
+ CF_HC_INIT,
+ CF_HC_CONNECT,
+ CF_HC_SUCCESS,
+ CF_HC_FAILURE
+} cf_hc_state;
+
+struct cf_hc_baller {
+ const char *name;
+ struct Curl_cfilter *cf;
+ CURLcode result;
+ struct curltime started;
+ int reply_ms;
+ bool enabled;
+};
+
+static void cf_hc_baller_reset(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;
+ }
+ b->result = CURLE_OK;
+ b->reply_ms = -1;
+}
+
+static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
+{
+ return b->enabled && b->cf && !b->result;
+}
+
+static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
+{
+ return !!b->cf;
+}
+
+static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
+ struct Curl_easy *data)
+{
+ if(b->reply_ms < 0)
+ b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
+ &b->reply_ms, NULL);
+ return b->reply_ms;
+}
+
+static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
+ const struct Curl_easy *data)
+{
+ return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
+}
+
+struct cf_hc_ctx {
+ cf_hc_state state;
+ const struct Curl_dns_entry *remotehost;
+ struct curltime started; /* when connect started */
+ CURLcode result; /* overall result */
+ struct cf_hc_baller h3_baller;
+ struct cf_hc_baller h21_baller;
+ int soft_eyeballs_timeout_ms;
+ int hard_eyeballs_timeout_ms;
+};
+
+static void cf_hc_baller_init(struct cf_hc_baller *b,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *name,
+ int transport)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ struct Curl_cfilter *save = cf->next;
+
+ b->name = name;
+ cf->next = NULL;
+ b->started = Curl_now();
+ b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
+ transport, CURL_CF_SSL_ENABLE);
+ b->cf = cf->next;
+ cf->next = save;
+}
+
+static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ struct Curl_cfilter *save = cf->next;
+
+ cf->next = b->cf;
+ b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ b->cf = cf->next; /* it might mutate */
+ cf->next = save;
+ return b->result;
+}
+
+static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
+ if(ctx) {
+ cf_hc_baller_reset(&ctx->h3_baller, data);
+ cf_hc_baller_reset(&ctx->h21_baller, data);
+ ctx->state = CF_HC_INIT;
+ ctx->result = CURLE_OK;
+ ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
+ ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
+ }
+}
+
+static CURLcode baller_connected(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_hc_baller *winner)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(winner->cf);
+ if(winner != &ctx->h3_baller)
+ cf_hc_baller_reset(&ctx->h3_baller, data);
+ if(winner != &ctx->h21_baller)
+ cf_hc_baller_reset(&ctx->h21_baller, data);
+
+ DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
+ winner->name, (int)Curl_timediff(Curl_now(), winner->started),
+ cf_hc_baller_reply_ms(winner, data)));
+ cf->next = winner->cf;
+ winner->cf = NULL;
+
+ switch(cf->conn->alpn) {
+ case CURL_HTTP_VERSION_3:
+ infof(data, "using HTTP/3");
+ break;
+ case CURL_HTTP_VERSION_2:
+#ifdef USE_NGHTTP2
+ /* Using nghttp2, we add the filter "below" us, so when the conn
+ * closes, we tear it down for a fresh reconnect */
+ result = Curl_http2_switch_at(cf, data);
+ if(result) {
+ ctx->state = CF_HC_FAILURE;
+ ctx->result = result;
+ return result;
+ }
+#endif
+ infof(data, "using HTTP/2");
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ infof(data, "using HTTP/1.1");
+ break;
+ default:
+ infof(data, "using HTTP/1.x");
+ break;
+ }
+ ctx->state = CF_HC_SUCCESS;
+ cf->connected = TRUE;
+ Curl_conn_cf_cntrl(cf->next, data, TRUE,
+ CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+ return result;
+}
+
+
+static bool time_to_start_h21(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct curltime now)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ timediff_t elapsed_ms;
+
+ if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller))
+ return FALSE;
+
+ if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller))
+ return TRUE;
+
+ elapsed_ms = Curl_timediff(now, ctx->started);
+ if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
+ DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21",
+ ctx->hard_eyeballs_timeout_ms));
+ return TRUE;
+ }
+
+ if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
+ if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) {
+ DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not "
+ "seen any data, starting h21",
+ ctx->soft_eyeballs_timeout_ms));
+ return TRUE;
+ }
+ /* set the effective hard timeout again */
+ Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
+ EXPIRE_ALPN_EYEBALLS);
+ }
+ return FALSE;
+}
+
+static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ struct curltime now;
+ CURLcode result = CURLE_OK;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+ switch(ctx->state) {
+ case CF_HC_INIT:
+ DEBUGASSERT(!ctx->h3_baller.cf);
+ DEBUGASSERT(!ctx->h21_baller.cf);
+ DEBUGASSERT(!cf->next);
+ DEBUGF(LOG_CF(data, cf, "connect, init"));
+ ctx->started = now;
+ if(ctx->h3_baller.enabled) {
+ cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC);
+ if(ctx->h21_baller.enabled)
+ Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
+ }
+ else if(ctx->h21_baller.enabled)
+ cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP);
+ ctx->state = CF_HC_CONNECT;
+ /* FALLTHROUGH */
+
+ case CF_HC_CONNECT:
+ if(cf_hc_baller_is_active(&ctx->h3_baller)) {
+ result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done);
+ if(!result && *done) {
+ result = baller_connected(cf, data, &ctx->h3_baller);
+ goto out;
+ }
+ }
+
+ if(time_to_start_h21(cf, data, now)) {
+ cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP);
+ }
+
+ if(cf_hc_baller_is_active(&ctx->h21_baller)) {
+ DEBUGF(LOG_CF(data, cf, "connect, check h21"));
+ result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done);
+ if(!result && *done) {
+ result = baller_connected(cf, data, &ctx->h21_baller);
+ goto out;
+ }
+ }
+
+ if((!ctx->h3_baller.enabled || ctx->h3_baller.result) &&
+ (!ctx->h21_baller.enabled || ctx->h21_baller.result)) {
+ /* both failed or disabled. we give up */
+ DEBUGF(LOG_CF(data, cf, "connect, all failed"));
+ result = ctx->result = ctx->h3_baller.enabled?
+ ctx->h3_baller.result : ctx->h21_baller.result;
+ ctx->state = CF_HC_FAILURE;
+ goto out;
+ }
+ result = CURLE_OK;
+ *done = FALSE;
+ break;
+
+ case CF_HC_FAILURE:
+ result = ctx->result;
+ cf->connected = FALSE;
+ *done = FALSE;
+ break;
+
+ case CF_HC_SUCCESS:
+ result = CURLE_OK;
+ cf->connected = TRUE;
+ *done = TRUE;
+ break;
+ }
+
+out:
+ DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+ return result;
+}
+
+static int cf_hc_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ size_t i, j, s;
+ int brc, rc = GETSOCK_BLANK;
+ curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE];
+ struct cf_hc_baller *ballers[2];
+
+ if(cf->connected)
+ return cf->next->cft->get_select_socks(cf->next, data, socks);
+
+ ballers[0] = &ctx->h3_baller;
+ ballers[1] = &ctx->h21_baller;
+ for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
+ struct cf_hc_baller *b = ballers[i];
+ if(!cf_hc_baller_is_active(b))
+ continue;
+ brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
+ DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc));
+ if(!brc)
+ continue;
+ for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
+ if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) {
+ socks[s] = bsocks[j];
+ if(brc & GETSOCK_WRITESOCK(j))
+ rc |= GETSOCK_WRITESOCK(s);
+ if(brc & GETSOCK_READSOCK(j))
+ rc |= GETSOCK_READSOCK(s);
+ s++;
+ }
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc));
+ return rc;
+}
+
+static bool cf_hc_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
+ if(cf->connected)
+ return cf->next->cft->has_data_pending(cf->next, data);
+
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
+ return cf_hc_baller_data_pending(&ctx->h3_baller, data)
+ || cf_hc_baller_data_pending(&ctx->h21_baller, data);
+}
+
+static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf_hc_reset(cf, data);
+ cf->connected = FALSE;
+
+ if(cf->next) {
+ cf->next->cft->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;
+
+ (void)data;
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ cf_hc_reset(cf, data);
+ Curl_safefree(ctx);
+}
+
+struct Curl_cftype Curl_cft_http_connect = {
+ "HTTPS-CONNECT",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_hc_destroy,
+ cf_hc_connect,
+ cf_hc_close,
+ Curl_cf_def_get_host,
+ cf_hc_get_select_socks,
+ cf_hc_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_hc_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_hc_ctx *ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->remotehost = remotehost;
+ ctx->h3_baller.enabled = try_h3;
+ ctx->h21_baller.enabled = try_h21;
+
+ result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
+ if(result)
+ goto out;
+ ctx = NULL;
+ cf_hc_reset(cf, data);
+
+out:
+ *pcf = result? NULL : cf;
+ free(ctx);
+ return result;
+}
+
+CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+out:
+ return result;
+}
+
+CURLcode
+Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+out:
+ return result;
+}
+
+CURLcode Curl_cf_https_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost)
+{
+ bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */
+ CURLcode result = CURLE_OK;
+
+ (void)sockindex;
+ (void)remotehost;
+
+ if(!conn->bits.tls_enable_alpn)
+ goto out;
+
+ if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+ result = Curl_conn_may_http3(data, conn);
+ if(result) /* can't do it */
+ goto out;
+ try_h3 = TRUE;
+ try_h21 = FALSE;
+ }
+ else if(data->state.httpwant >= CURL_HTTP_VERSION_3) {
+ /* We assume that silently not even trying H3 is ok here */
+ /* TODO: should we fail instead? */
+ try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK);
+ try_h21 = TRUE;
+ }
+
+ result = Curl_cf_http_connect_add(data, conn, sockindex, remotehost,
+ try_h3, try_h21);
+out:
+ return result;
+}
+
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
diff --git a/Utilities/cmcurl/lib/cf-http.h b/Utilities/cmcurl/lib/cf-http.h
new file mode 100644
index 0000000000..6a39527317
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-http.h
@@ -0,0 +1,58 @@
+#ifndef HEADER_CURL_CF_HTTP_H
+#define HEADER_CURL_CF_HTTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_cftype;
+struct Curl_dns_entry;
+
+extern struct Curl_cftype Curl_cft_http_connect;
+
+CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21);
+
+CURLcode
+Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21);
+
+
+CURLcode Curl_cf_https_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost);
+
+
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
+#endif /* HEADER_CURL_CF_HTTP_H */
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
new file mode 100644
index 0000000000..2549f3456b
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -0,0 +1,1908 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* <netinet/tcp.h> may need it */
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h> /* for sockaddr_un */
+#endif
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "if2ip.h"
+#include "strerror.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "select.h"
+#include "url.h" /* for Curl_safefree() */
+#include "multiif.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "inet_ntop.h"
+#include "inet_pton.h"
+#include "progress.h"
+#include "warnless.h"
+#include "conncache.h"
+#include "multihandle.h"
+#include "share.h"
+#include "version_win32.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
+{
+#if defined(TCP_NODELAY)
+ curl_socklen_t onoff = (curl_socklen_t) 1;
+ int level = IPPROTO_TCP;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+#else
+ (void) data;
+#endif
+
+ if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
+ sizeof(onoff)) < 0)
+ infof(data, "Could not set TCP_NODELAY: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#else
+ (void)data;
+ (void)sockfd;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+ sending data to a dead peer (instead of relying on the 4th argument to send
+ being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
+ systems? */
+static void nosigpipe(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int onoff = 1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
+ sizeof(onoff)) < 0) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+ infof(data, "Could not set SO_NOSIGPIPE: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#endif
+ }
+}
+#else
+#define nosigpipe(x,y) Curl_nop_stmt
+#endif
+
+#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
+/* DragonFlyBSD and Windows use millisecond units */
+#define KEEPALIVE_FACTOR(x) (x *= 1000)
+#else
+#define KEEPALIVE_FACTOR(x)
+#endif
+
+#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
+#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
+
+struct tcp_keepalive {
+ u_long onoff;
+ u_long keepalivetime;
+ u_long keepaliveinterval;
+};
+#endif
+
+static void
+tcpkeepalive(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int optval = data->set.tcp_keepalive?1:0;
+
+ /* only set IDLE and INTVL if setting KEEPALIVE is successful */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
+ }
+ else {
+#if defined(SIO_KEEPALIVE_VALS)
+ struct tcp_keepalive vals;
+ DWORD dummy;
+ vals.onoff = 1;
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepalivetime = optval;
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepaliveinterval = optval;
+ if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
+ NULL, 0, &dummy, NULL, NULL) != 0) {
+ infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
+ (int)sockfd, WSAGetLastError());
+ }
+#else
+#ifdef TCP_KEEPIDLE
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
+ }
+#elif defined(TCP_KEEPALIVE)
+ /* Mac OS X style */
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
+ }
+#endif
+#ifdef TCP_KEEPINTVL
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
+ }
+#endif
+#endif
+ }
+}
+
+void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ /*
+ * The Curl_sockaddr_ex structure is basically 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.
+ */
+ dest->family = ai->ai_family;
+ switch(transport) {
+ case TRNSPRT_TCP:
+ dest->socktype = SOCK_STREAM;
+ dest->protocol = IPPROTO_TCP;
+ break;
+ case TRNSPRT_UNIX:
+ dest->socktype = SOCK_STREAM;
+ dest->protocol = IPPROTO_IP;
+ break;
+ default: /* UDP and QUIC */
+ dest->socktype = SOCK_DGRAM;
+ dest->protocol = IPPROTO_UDP;
+ break;
+ }
+ dest->addrlen = ai->ai_addrlen;
+
+ if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
+ dest->addrlen = sizeof(struct Curl_sockaddr_storage);
+ memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen);
+}
+
+static CURLcode socket_open(struct Curl_easy *data,
+ struct Curl_sockaddr_ex *addr,
+ curl_socket_t *sockfd)
+{
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ if(data->set.fopensocket) {
+ /*
+ * If the opensocket callback is set, all the destination address
+ * information is passed to the callback. Depending on this information the
+ * callback may opt to abort the connection, this is indicated returning
+ * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
+ * the callback returns a valid socket the destination address information
+ * might have been changed and this 'new' address will actually be used
+ * here to connect.
+ */
+ Curl_set_in_callback(data, true);
+ *sockfd = data->set.fopensocket(data->set.opensocket_client,
+ CURLSOCKTYPE_IPCXN,
+ (struct curl_sockaddr *)addr);
+ Curl_set_in_callback(data, false);
+ }
+ else {
+ /* opensocket callback not set, so simply create the socket now */
+ *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+ if(!*sockfd && addr->socktype == SOCK_DGRAM) {
+ /* This is icky and seems, at least, to happen on macOS:
+ * we get sockfd == 0 and if called again, we get a valid one > 0.
+ * If we close the 0, we sometimes get failures in multi poll, as
+ * 0 seems also be the fd for the sockpair used for WAKEUP polling.
+ * Very strange. Maybe this code should be ifdef'ed for macOS, but
+ * on "real" OS, fd 0 is stdin and we never see that. So...
+ */
+ fake_sclose(*sockfd);
+ *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+ DEBUGF(infof(data, "QUIRK: UDP socket() gave handle 0, 2nd attempt %d",
+ (int)*sockfd));
+ }
+ }
+
+ if(*sockfd == CURL_SOCKET_BAD)
+ /* no socket, no connection */
+ return CURLE_COULDNT_CONNECT;
+
+#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+ if(data->conn->scope_id && (addr->family == AF_INET6)) {
+ struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
+ sa6->sin6_scope_id = data->conn->scope_id;
+ }
+#endif
+ return CURLE_OK;
+}
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * 'addr' should be a pointer to the correct struct to get data back, or NULL.
+ * 'sockfd' must be a pointer to a socket descriptor.
+ *
+ * If the open socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket_open(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ int transport,
+ curl_socket_t *sockfd)
+{
+ struct Curl_sockaddr_ex dummy;
+
+ if(!addr)
+ /* if the caller doesn't want info back, use a local temp copy */
+ addr = &dummy;
+
+ Curl_sock_assign_addr(addr, ai, transport);
+ return socket_open(data, addr, sockfd);
+}
+
+static int socket_close(struct Curl_easy *data, struct connectdata *conn,
+ int use_callback, curl_socket_t sock)
+{
+ if(use_callback && conn && conn->fclosesocket) {
+ int rc;
+ Curl_multi_closed(data, sock);
+ Curl_set_in_callback(data, true);
+ rc = conn->fclosesocket(conn->closesocket_client, sock);
+ Curl_set_in_callback(data, false);
+ return rc;
+ }
+
+ if(conn)
+ /* tell the multi-socket code about this */
+ Curl_multi_closed(data, sock);
+
+ sclose(sock);
+
+ return 0;
+}
+
+/*
+ * Close a socket.
+ *
+ * 'conn' can be NULL, beware!
+ */
+int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock)
+{
+ return socket_close(data, conn, FALSE, sock);
+}
+
+bool Curl_socket_is_dead(curl_socket_t sock)
+{
+ int sval;
+ bool ret_val = TRUE;
+
+ sval = SOCKET_READABLE(sock, 0);
+ if(sval == 0)
+ /* timeout */
+ ret_val = FALSE;
+
+ return ret_val;
+}
+
+
+#ifdef USE_WINSOCK
+/* When you run a program that uses the Windows Sockets API, you may
+ experience slow performance when you copy data to a TCP server.
+
+ https://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+ The problem described in this knowledge-base is applied only to pre-Vista
+ Windows. Following function trying to detect OS version and skips
+ SO_SNDBUF adjustment for Windows Vista and above.
+*/
+#define DETECT_OS_NONE 0
+#define DETECT_OS_PREVISTA 1
+#define DETECT_OS_VISTA_OR_LATER 2
+
+void Curl_sndbufset(curl_socket_t sockfd)
+{
+ int val = CURL_MAX_WRITE_SIZE + 32;
+ int curval = 0;
+ int curlen = sizeof(curval);
+
+ static int detectOsState = DETECT_OS_NONE;
+
+ if(detectOsState == DETECT_OS_NONE) {
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ detectOsState = DETECT_OS_VISTA_OR_LATER;
+ else
+ detectOsState = DETECT_OS_PREVISTA;
+ }
+
+ if(detectOsState == DETECT_OS_VISTA_OR_LATER)
+ return;
+
+ if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
+ if(curval > val)
+ return;
+
+ setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+}
+#endif
+
+static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd, int af, unsigned int scope)
+{
+ struct Curl_sockaddr_storage sa;
+ struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
+ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
+#endif
+
+ struct Curl_dns_entry *h = NULL;
+ unsigned short port = data->set.localport; /* use this port number, 0 for
+ "random" */
+ /* how many port numbers to try to bind to, increasing one at a time */
+ int portnum = data->set.localportrange;
+ const char *dev = data->set.str[STRING_DEVICE];
+ int error;
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ int on = 1;
+#endif
+#ifndef ENABLE_IPV6
+ (void)scope;
+#endif
+
+ /*************************************************************
+ * Select device to bind socket to
+ *************************************************************/
+ if(!dev && !port)
+ /* no local kind of binding was requested */
+ return CURLE_OK;
+
+ memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
+
+ if(dev && (strlen(dev)<255) ) {
+ char myhost[256] = "";
+ int done = 0; /* -1 for error, 1 for address found */
+ bool is_interface = FALSE;
+ bool is_host = FALSE;
+ static const char *if_prefix = "if!";
+ static const char *host_prefix = "host!";
+
+ if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
+ dev += strlen(if_prefix);
+ is_interface = TRUE;
+ }
+ else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
+ dev += strlen(host_prefix);
+ is_host = TRUE;
+ }
+
+ /* interface */
+ if(!is_host) {
+#ifdef SO_BINDTODEVICE
+ /* I am not sure any other OSs than Linux that provide this feature,
+ * and at the least I cannot test. --Ben
+ *
+ * This feature allows one to tightly bind the local socket to a
+ * particular interface. This will force even requests to other
+ * local interfaces to go out the external interface.
+ *
+ *
+ * Only bind to the interface when specified as interface, not just
+ * as a hostname or ip address.
+ *
+ * interface might be a VRF, eg: vrf-blue, which means it cannot be
+ * converted to an IP address and would fail Curl_if2ip. Simply try
+ * to use it straight away.
+ */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
+ /* This is typically "errno 1, error: Operation not permitted" if
+ * you're not running as root or another suitable privileged
+ * user.
+ * If it succeeds it means the parameter was a valid interface and
+ * not an IP address. Return immediately.
+ */
+ return CURLE_OK;
+ }
+#endif
+
+ switch(Curl_if2ip(af,
+#ifdef ENABLE_IPV6
+ scope, conn->scope_id,
+#endif
+ dev, myhost, sizeof(myhost))) {
+ case IF2IP_NOT_FOUND:
+ if(is_interface) {
+ /* Do not fall back to treating it as a host name */
+ failf(data, "Couldn't bind to interface '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ /* Signal the caller to try another address family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ case IF2IP_FOUND:
+ is_interface = TRUE;
+ /*
+ * We now have the numerical IP address in the 'myhost' buffer
+ */
+ infof(data, "Local Interface %s is ip %s using address family %i",
+ dev, myhost, af);
+ done = 1;
+ break;
+ }
+ }
+ if(!is_interface) {
+ /*
+ * This was not an interface, resolve the name as a host name
+ * or IP number
+ *
+ * Temporarily force name resolution to use only the address type
+ * of the connection. The resolve functions should really be changed
+ * to take a type parameter instead.
+ */
+ unsigned char ipver = conn->ip_version;
+ int rc;
+
+ if(af == AF_INET)
+ conn->ip_version = CURL_IPRESOLVE_V4;
+#ifdef ENABLE_IPV6
+ else if(af == AF_INET6)
+ conn->ip_version = CURL_IPRESOLVE_V6;
+#endif
+
+ rc = Curl_resolv(data, dev, 0, FALSE, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_resolver_wait_resolv(data, &h);
+ conn->ip_version = ipver;
+
+ if(h) {
+ /* 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",
+ dev, af, myhost, h->addr->ai_family);
+ Curl_resolv_unlock(data, h);
+ if(af != h->addr->ai_family) {
+ /* bad IP version combo, signal the caller to try another address
+ family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ done = 1;
+ }
+ else {
+ /*
+ * provided dev was no interface (or interfaces are not supported
+ * e.g. solaris) no ip address and no domain we fail here
+ */
+ done = -1;
+ }
+ }
+
+ if(done > 0) {
+#ifdef ENABLE_IPV6
+ /* IPv6 address */
+ if(af == AF_INET6) {
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ char *scope_ptr = strchr(myhost, '%');
+ if(scope_ptr)
+ *(scope_ptr++) = '\0';
+#endif
+ if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ if(scope_ptr) {
+ /* The "myhost" string either comes from Curl_if2ip or from
+ Curl_printable_address. The latter returns only numeric scope
+ IDs and the former returns none at all. So the scope ID, if
+ present, is known to be numeric */
+ unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
+ if(scope_id > UINT_MAX)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ si6->sin6_scope_id = (unsigned int)scope_id;
+ }
+#endif
+ }
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ /* IPv4 address */
+ if((af == AF_INET) &&
+ (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+
+ if(done < 1) {
+ /* errorbuf is set false so failf will overwrite any message already in
+ the error buffer, so the user receives this error message instead of a
+ generic resolve error. */
+ data->state.errorbuf = FALSE;
+ failf(data, "Couldn't bind to '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ }
+ else {
+ /* no device was given, prepare sa to match af's needs */
+#ifdef ENABLE_IPV6
+ if(af == AF_INET6) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ if(af == AF_INET) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
+#endif
+ for(;;) {
+ if(bind(sockfd, sock, sizeof_sa) >= 0) {
+ /* we succeeded to bind */
+ struct Curl_sockaddr_storage add;
+ curl_socklen_t size = sizeof(add);
+ memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
+ if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return CURLE_INTERFACE_FAILED;
+ }
+ infof(data, "Local port: %hu", port);
+ conn->bits.bound = TRUE;
+ return CURLE_OK;
+ }
+
+ if(--portnum > 0) {
+ port++; /* try next port */
+ if(port == 0)
+ break;
+ infof(data, "Bind to local port %hu failed, trying next", port - 1);
+ /* We re-use/clobber the port variable here below */
+ if(sock->sa_family == AF_INET)
+ si4->sin_port = ntohs(port);
+#ifdef ENABLE_IPV6
+ else
+ si6->sin6_port = ntohs(port);
+#endif
+ }
+ else
+ break;
+ }
+ {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "bind failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ }
+
+ return CURLE_INTERFACE_FAILED;
+}
+
+/*
+ * verifyconnect() returns TRUE if the connect really has happened.
+ */
+static bool verifyconnect(curl_socket_t sockfd, int *error)
+{
+ bool rc = TRUE;
+#ifdef SO_ERROR
+ int err = 0;
+ curl_socklen_t errSize = sizeof(err);
+
+#ifdef WIN32
+ /*
+ * In October 2003 we effectively nullified this function on Windows due to
+ * problems with it using all CPU in multi-threaded cases.
+ *
+ * In May 2004, we bring it back to offer more info back on connect failures.
+ * Gisle Vanem could reproduce the former problems with this function, but
+ * could avoid them by adding this SleepEx() call below:
+ *
+ * "I don't have Rational Quantify, but the hint from his post was
+ * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
+ * just Sleep(0) would be enough?) would release whatever
+ * mutex/critical-section the ntdll call is waiting on.
+ *
+ * Someone got to verify this on Win-NT 4.0, 2000."
+ */
+
+#ifdef _WIN32_WCE
+ Sleep(0);
+#else
+ SleepEx(0, FALSE);
+#endif
+
+#endif
+
+ if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
+ err = SOCKERRNO;
+#ifdef _WIN32_WCE
+ /* Old WinCE versions don't support SO_ERROR */
+ if(WSAENOPROTOOPT == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+#if defined(EBADIOCTL) && defined(__minix)
+ /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
+ if(EBADIOCTL == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+ if((0 == err) || (EISCONN == err))
+ /* we are connected, awesome! */
+ rc = TRUE;
+ else
+ /* This wasn't a successful connect */
+ rc = FALSE;
+ if(error)
+ *error = err;
+#else
+ (void)sockfd;
+ if(error)
+ *error = SOCKERRNO;
+#endif
+ return rc;
+}
+
+CURLcode Curl_socket_connect_result(struct Curl_easy *data,
+ const char *ipaddress, int error)
+{
+ char buffer[STRERROR_LEN];
+
+ switch(error) {
+ case EINPROGRESS:
+ case EWOULDBLOCK:
+#if defined(EAGAIN)
+#if (EAGAIN) != (EWOULDBLOCK)
+ /* On some platforms EAGAIN and EWOULDBLOCK are the
+ * same value, and on others they are different, hence
+ * the odd #if
+ */
+ case EAGAIN:
+#endif
+#endif
+ return CURLE_OK;
+
+ default:
+ /* unknown error, fallthrough and try another address! */
+ infof(data, "Immediate connect fail for %s: %s",
+ ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+ data->state.os_errno = error;
+ /* connect failed */
+ return CURLE_COULDNT_CONNECT;
+ }
+}
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+struct io_buffer {
+ char *bufr;
+ size_t allc; /* size of the current allocation */
+ size_t head; /* bufr index for next read */
+ size_t tail; /* bufr index for next write */
+};
+
+static void io_buffer_reset(struct io_buffer *iob)
+{
+ if(iob->bufr)
+ free(iob->bufr);
+ memset(iob, 0, sizeof(*iob));
+}
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
+struct cf_socket_ctx {
+ int transport;
+ struct Curl_sockaddr_ex addr; /* address to connect to */
+ curl_socket_t sock; /* current attempt socket */
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ struct io_buffer recv_buffer;
+#endif
+ char r_ip[MAX_IPADR_LEN]; /* remote IP as string */
+ int r_port; /* remote port number */
+ char l_ip[MAX_IPADR_LEN]; /* local IP as string */
+ int l_port; /* local port number */
+ struct curltime started_at; /* when socket was created */
+ struct curltime connected_at; /* when socket connected/got first byte */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ int error; /* errno of last failure or 0 */
+ BIT(got_first_byte); /* if first byte was received */
+ BIT(accepted); /* socket was accepted, not connected */
+ BIT(active);
+};
+
+static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->sock = CURL_SOCKET_BAD;
+ ctx->transport = transport;
+ Curl_sock_assign_addr(&ctx->addr, ai, transport);
+}
+
+static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ if(ctx && CURL_SOCKET_BAD != ctx->sock) {
+ if(ctx->active) {
+ /* We share our socket at cf->conn->sock[cf->sockindex] when active.
+ * If it is no longer there, someone has stolen (and hopefully
+ * closed it) and we just forget about it.
+ */
+ if(ctx->sock == cf->conn->sock[cf->sockindex]) {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, active)",
+ (int)ctx->sock));
+ socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
+ cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
+ "conn->sock[], discarding", (int)ctx->sock));
+ /* TODO: we do not want this to happen. Need to check which
+ * code is messing with conn->sock[cf->sockindex] */
+ }
+ ctx->sock = CURL_SOCKET_BAD;
+ if(cf->sockindex == FIRSTSOCKET)
+ cf->conn->remote_addr = NULL;
+ }
+ else {
+ /* this is our local socket, we did never publish it */
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, not active)",
+ (int)ctx->sock));
+ sclose(ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ io_buffer_reset(&ctx->recv_buffer);
+#endif
+ ctx->active = FALSE;
+ memset(&ctx->started_at, 0, sizeof(ctx->started_at));
+ memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
+ }
+
+ cf->connected = FALSE;
+}
+
+static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ cf_socket_close(cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ free(ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode set_local_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+#ifdef HAVE_GETSOCKNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssloc;
+ curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+
+ memset(&ssloc, 0, sizeof(ssloc));
+ if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
+ int error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+ ctx->l_ip, &ctx->l_port)) {
+ failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+#else
+ (void)data;
+ ctx->l_ip[0] = 0;
+ ctx->l_port = -1;
+#endif
+ return CURLE_OK;
+}
+
+static CURLcode set_remote_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ /* store remote address and port used in this connection attempt */
+ if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
+ ctx->r_ip, &ctx->r_port)) {
+ char buffer[STRERROR_LEN];
+
+ ctx->error = errno;
+ /* malformed address or bug in inet_ntop, try next address */
+ failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_socket_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int error = 0;
+ bool isconnected = FALSE;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+ bool is_tcp;
+ const char *ipmsg;
+
+ (void)data;
+ DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
+ ctx->started_at = Curl_now();
+ result = socket_open(data, &ctx->addr, &ctx->sock);
+ if(result)
+ goto out;
+
+ result = set_remote_ip(cf, data);
+ if(result)
+ goto out;
+
+#ifdef ENABLE_IPV6
+ if(ctx->addr.family == AF_INET6)
+ ipmsg = " Trying [%s]:%d...";
+ else
+#endif
+ ipmsg = " Trying %s:%d...";
+ infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+
+#ifdef ENABLE_IPV6
+ is_tcp = (ctx->addr.family == AF_INET
+ || ctx->addr.family == AF_INET6) &&
+ ctx->addr.socktype == SOCK_STREAM;
+#else
+ is_tcp = (ctx->addr.family == AF_INET) &&
+ ctx->addr.socktype == SOCK_STREAM;
+#endif
+ if(is_tcp && data->set.tcp_nodelay)
+ tcpnodelay(data, ctx->sock);
+
+ nosigpipe(data, ctx->sock);
+
+ Curl_sndbufset(ctx->sock);
+
+ if(is_tcp && data->set.tcp_keepalive)
+ tcpkeepalive(data, ctx->sock);
+
+ if(data->set.fsockopt) {
+ /* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
+ error = data->set.fsockopt(data->set.sockopt_client,
+ ctx->sock,
+ CURLSOCKTYPE_IPCXN);
+ Curl_set_in_callback(data, false);
+
+ if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
+ isconnected = TRUE;
+ else if(error) {
+ result = CURLE_ABORTED_BY_CALLBACK;
+ goto out;
+ }
+ }
+
+ /* possibly bind the local end to an IP, interface or port */
+ if(ctx->addr.family == AF_INET
+#ifdef ENABLE_IPV6
+ || ctx->addr.family == AF_INET6
+#endif
+ ) {
+ result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
+ Curl_ipv6_scope(&ctx->addr.sa_addr));
+ if(result) {
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ /* The address family is not supported on this interface.
+ We can continue trying addresses */
+ result = CURLE_COULDNT_CONNECT;
+ }
+ goto out;
+ }
+ }
+
+ /* set socket non-blocking */
+ (void)curlx_nonblock(ctx->sock, TRUE);
+
+out:
+ if(result) {
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ }
+ else if(isconnected) {
+ set_local_ip(cf, data);
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ }
+ DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%d", result, ctx->sock));
+ return result;
+}
+
+static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool is_tcp_fastopen)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+#ifdef TCP_FASTOPEN_CONNECT
+ int optval = 1;
+#endif
+ int rc = -1;
+
+ (void)data;
+ if(is_tcp_fastopen) {
+#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
+# if defined(HAVE_BUILTIN_AVAILABLE)
+ /* while connectx function is available since macOS 10.11 / iOS 9,
+ it did not have the interface declared correctly until
+ Xcode 9 / macOS SDK 10.13 */
+ if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
+ sa_endpoints_t endpoints;
+ endpoints.sae_srcif = 0;
+ endpoints.sae_srcaddr = NULL;
+ endpoints.sae_srcaddrlen = 0;
+ endpoints.sae_dstaddr = &ctx->addr.sa_addr;
+ endpoints.sae_dstaddrlen = ctx->addr.addrlen;
+
+ rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
+ CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
+ NULL, 0, NULL, NULL);
+ }
+ else {
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ }
+# else
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+# endif /* HAVE_BUILTIN_AVAILABLE */
+#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
+ if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
+ (void *)&optval, sizeof(optval)) < 0)
+ infof(data, "Failed to enable TCP Fast Open on fd %d", ctx->sock);
+
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+#elif defined(MSG_FASTOPEN) /* old Linux */
+ if(cf->conn->given->flags & PROTOPT_SSL)
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ else
+ rc = 0; /* Do nothing */
+#endif
+ }
+ else {
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ }
+ return rc;
+}
+
+static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+ int rc = 0;
+
+ (void)data;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* TODO: need to support blocking connect? */
+ if(blocking)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ *done = FALSE; /* a very negative world view is best */
+ if(ctx->sock == CURL_SOCKET_BAD) {
+
+ result = cf_socket_open(cf, data);
+ if(result)
+ goto out;
+
+ /* Connect TCP socket */
+ rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
+ if(-1 == rc) {
+ result = Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+ goto out;
+ }
+ }
+
+#ifdef mpeix
+ /* Call this function once now, and ignore the results. We do this to
+ "clear" the error state on the socket so that we can later read it
+ reliably. This is reported necessary on the MPE/iX operating
+ system. */
+ (void)verifyconnect(ctx->sock, NULL);
+#endif
+ /* check socket for connect */
+ rc = SOCKET_WRITABLE(ctx->sock, 0);
+
+ if(rc == 0) { /* no connection yet */
+ DEBUGF(LOG_CF(data, cf, "not connected yet"));
+ return CURLE_OK;
+ }
+ else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
+ if(verifyconnect(ctx->sock, &ctx->error)) {
+ /* we are connected with TCP, awesome! */
+ ctx->connected_at = Curl_now();
+ set_local_ip(cf, data);
+ *done = TRUE;
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "connected"));
+ return CURLE_OK;
+ }
+ }
+ else if(rc & CURL_CSELECT_ERR) {
+ (void)verifyconnect(ctx->sock, &ctx->error);
+ result = CURLE_COULDNT_CONNECT;
+ }
+
+out:
+ if(result) {
+ if(ctx->error) {
+ data->state.os_errno = ctx->error;
+ SET_SOCKERRNO(ctx->error);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ {
+ char buffer[STRERROR_LEN];
+ infof(data, "connect to %s port %u failed: %s",
+ ctx->r_ip, ctx->r_port,
+ Curl_strerror(ctx->error, buffer, sizeof(buffer)));
+ }
+#endif
+ }
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ *done = FALSE;
+ }
+ return result;
+}
+
+static void cf_socket_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+}
+
+static int cf_socket_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int rc = GETSOCK_BLANK;
+
+ (void)data;
+ if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) {
+ socks[0] = ctx->sock;
+ rc |= GETSOCK_WRITESOCK(0);
+ }
+
+ return rc;
+}
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+
+static CURLcode pre_receive_plain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ struct io_buffer * const iob = &ctx->recv_buffer;
+
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. However, skip this, if buffer is already full. */
+ if((cf->conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
+ cf->conn->recv[cf->sockindex] == Curl_conn_recv &&
+ (!iob->bufr || (iob->allc > iob->tail))) {
+ const int readymask = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD,
+ CURL_SOCKET_BAD, 0);
+ if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
+ size_t bytestorecv = iob->allc - iob->tail;
+ ssize_t nread;
+ /* Have some incoming data */
+ if(!iob->bufr) {
+ /* Use buffer double default size for intermediate buffer */
+ iob->allc = 2 * data->set.buffer_size;
+ iob->bufr = malloc(iob->allc);
+ if(!iob->bufr)
+ return CURLE_OUT_OF_MEMORY;
+ iob->tail = 0;
+ iob->head = 0;
+ bytestorecv = iob->allc;
+ }
+
+ nread = sread(ctx->sock, iob->bufr + iob->tail, bytestorecv);
+ if(nread > 0)
+ iob->tail += (size_t)nread;
+ }
+ }
+ return CURLE_OK;
+}
+
+static ssize_t get_pre_recved(struct Curl_cfilter *cf, char *buf, size_t len)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ struct io_buffer * const iob = &ctx->recv_buffer;
+ size_t copysize;
+ if(!iob->bufr)
+ return 0;
+
+ DEBUGASSERT(iob->allc > 0);
+ DEBUGASSERT(iob->tail <= iob->allc);
+ DEBUGASSERT(iob->head <= iob->tail);
+ /* Check and process data that already received and storied in internal
+ intermediate buffer */
+ if(iob->tail > iob->head) {
+ copysize = CURLMIN(len, iob->tail - iob->head);
+ memcpy(buf, iob->bufr + iob->head, copysize);
+ iob->head += copysize;
+ }
+ else
+ copysize = 0; /* buffer was allocated, but nothing was received */
+
+ /* Free intermediate buffer if it has no unprocessed data */
+ if(iob->head == iob->tail)
+ io_buffer_reset(iob);
+
+ return (ssize_t)copysize;
+}
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
+static bool cf_socket_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int readable;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ if(ctx->recv_buffer.bufr && ctx->recv_buffer.allc &&
+ ctx->recv_buffer.tail > ctx->recv_buffer.head)
+ return TRUE;
+#endif
+
+ (void)data;
+ readable = SOCKET_READABLE(ctx->sock, 0);
+ return (readable > 0 && (readable & CURL_CSELECT_IN));
+}
+
+static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ curl_socket_t fdsave;
+ ssize_t nwritten;
+
+ *err = CURLE_OK;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. */
+ if(pre_receive_plain(cf, data)) {
+ *err = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+#endif
+
+ fdsave = cf->conn->sock[cf->sockindex];
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+
+#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
+ if(cf->conn->bits.tcp_fastopen) {
+ nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
+ &cf->conn->remote_addr->sa_addr,
+ cf->conn->remote_addr->addrlen);
+ cf->conn->bits.tcp_fastopen = FALSE;
+ }
+ else
+#endif
+ nwritten = swrite(ctx->sock, buf, len);
+
+ if(-1 == nwritten) {
+ int sockerr = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == 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 */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
+ (EINPROGRESS == sockerr)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *err = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Send failure: %s",
+ Curl_strerror(sockerr, buffer, sizeof(buffer)));
+ data->state.os_errno = sockerr;
+ *err = CURLE_SEND_ERROR;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d",
+ len, (int)nwritten, *err));
+ cf->conn->sock[cf->sockindex] = fdsave;
+ return nwritten;
+}
+
+static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ curl_socket_t fdsave;
+ ssize_t nread;
+
+ *err = CURLE_OK;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ /* Check and return data that already received and storied in internal
+ intermediate buffer */
+ nread = get_pre_recved(cf, buf, len);
+ if(nread > 0) {
+ *err = CURLE_OK;
+ return nread;
+ }
+#endif
+
+ fdsave = cf->conn->sock[cf->sockindex];
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+
+ nread = sread(ctx->sock, buf, len);
+
+ if(-1 == nread) {
+ int sockerr = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == 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 */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *err = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Recv failure: %s",
+ Curl_strerror(sockerr, buffer, sizeof(buffer)));
+ data->state.os_errno = sockerr;
+ *err = CURLE_RECV_ERROR;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
+ *err));
+ if(nread > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+ cf->conn->sock[cf->sockindex] = fdsave;
+ return nread;
+}
+
+static void conn_set_primary_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+#ifdef HAVE_GETPEERNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssrem;
+ curl_socklen_t plen;
+ int port;
+
+ plen = sizeof(ssrem);
+ memset(&ssrem, 0, plen);
+ if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
+ int error = SOCKERRNO;
+ failf(data, "getpeername() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+ cf->conn->primary_ip, &port)) {
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return;
+ }
+#else
+ cf->conn->primary_ip[0] = 0;
+ (void)data;
+#endif
+}
+
+static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ /* use this socket from now on */
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+ /* the first socket info gets set at conn and data */
+ if(cf->sockindex == FIRSTSOCKET) {
+ cf->conn->remote_addr = &ctx->addr;
+ #ifdef ENABLE_IPV6
+ cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
+ #endif
+ conn_set_primary_ip(cf, data);
+ set_local_ip(cf, data);
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ }
+ ctx->active = TRUE;
+}
+
+static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_CONN_INFO_UPDATE:
+ cf_socket_active(cf, data);
+ break;
+ case CF_CTRL_CONN_REPORT_STATS:
+ switch(ctx->transport) {
+ case TRNSPRT_UDP:
+ case TRNSPRT_QUIC:
+ /* Since UDP connected sockets work different from TCP, we use the
+ * time of the first byte from the peer as the "connect" time. */
+ if(ctx->got_first_byte) {
+ Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->connected_at);
+ break;
+ }
+ break;
+ case CF_CTRL_DATA_SETUP:
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ break;
+ }
+ return CURLE_OK;
+}
+
+static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int sval;
+
+ (void)data;
+ if(!ctx || ctx->sock == CURL_SOCKET_BAD)
+ return FALSE;
+
+ sval = SOCKET_READABLE(ctx->sock, 0);
+ if(sval == 0) {
+ /* timeout */
+ return TRUE;
+ }
+ else if(sval & CURL_CSELECT_ERR) {
+ /* socket is in an error state */
+ return FALSE;
+ }
+ else if(sval & CURL_CSELECT_IN) {
+ /* readable with no error. could still be closed */
+/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
+#ifdef MSG_PEEK
+ /* use the socket */
+ char buf;
+ if(recv((RECV_TYPE_ARG1)ctx->sock, (RECV_TYPE_ARG2)&buf,
+ (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
+ return FALSE; /* FIN received */
+ }
+#endif
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static CURLcode cf_socket_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_SOCKET:
+ DEBUGASSERT(pres2);
+ *((curl_socket_t *)pres2) = ctx->sock;
+ return CURLE_OK;
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+struct Curl_cftype Curl_cft_tcp = {
+ "TCP",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_TCP);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int rc;
+
+ /* QUIC needs a connected socket, nonblocking */
+ DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
+
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ if(-1 == rc) {
+ return Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+ }
+ set_local_ip(cf, data);
+ DEBUGF(LOG_CF(data, cf, "%s socket %d connected: [%s:%d] -> [%s:%d]",
+ (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
+ ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port));
+
+ (void)curlx_nonblock(ctx->sock, TRUE);
+ switch(ctx->addr.family) {
+#if defined(__linux__) && defined(IP_MTU_DISCOVER)
+ case AF_INET: {
+ int val = IP_PMTUDISC_DO;
+ (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
+ case AF_INET6: {
+ int val = IPV6_PMTUDISC_DO;
+ (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ *done = FALSE;
+ if(ctx->sock == CURL_SOCKET_BAD) {
+ result = cf_socket_open(cf, data);
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result));
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ goto out;
+ }
+
+ if(ctx->transport == TRNSPRT_QUIC) {
+ result = cf_udp_setup_quic(cf, data);
+ if(result)
+ goto out;
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d (%s:%d)",
+ ctx->sock, ctx->l_ip, ctx->l_port));
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d "
+ "(unconnected)", ctx->sock));
+ }
+ *done = TRUE;
+ cf->connected = TRUE;
+ }
+out:
+ return result;
+}
+
+struct Curl_cftype Curl_cft_udp = {
+ "UDP",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_udp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+/* this is the TCP filter which can also handle this case */
+struct Curl_cftype Curl_cft_unix = {
+ "UNIX",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_UNIX);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+struct Curl_cftype Curl_cft_tcp_accept = {
+ "TCP-ACCEPT",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_accept_connect,
+ cf_socket_close,
+ cf_socket_get_host, /* TODO: not accurate */
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct cf_socket_ctx *ctx = NULL;
+
+ /* replace any existing */
+ Curl_conn_cf_discard_all(data, conn, sockindex);
+ DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
+
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->transport = conn->transport;
+ ctx->sock = *s;
+ ctx->accepted = FALSE;
+ result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+ conn->sock[sockindex] = ctx->sock;
+ set_remote_ip(cf, data);
+ set_local_ip(cf, data);
+ ctx->active = TRUE;
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%d)", (int)ctx->sock));
+
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+ return result;
+}
+
+CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_socket_ctx *ctx = NULL;
+
+ cf = conn->cfilter[sockindex];
+ if(!cf || cf->cft != &Curl_cft_tcp_accept)
+ return CURLE_FAILED_INIT;
+
+ ctx = cf->ctx;
+ /* discard the listen socket */
+ socket_close(data, conn, TRUE, ctx->sock);
+ ctx->sock = *s;
+ conn->sock[sockindex] = ctx->sock;
+ set_remote_ip(cf, data);
+ set_local_ip(cf, data);
+ ctx->active = TRUE;
+ ctx->accepted = TRUE;
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_accepted_set(%d)", (int)ctx->sock));
+
+ return CURLE_OK;
+}
+
+bool Curl_cf_is_socket(struct Curl_cfilter *cf)
+{
+ return cf && (cf->cft == &Curl_cft_tcp ||
+ cf->cft == &Curl_cft_udp ||
+ cf->cft == &Curl_cft_unix ||
+ cf->cft == &Curl_cft_tcp_accept);
+}
+
+CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *psock,
+ const struct Curl_sockaddr_ex **paddr,
+ const char **pr_ip_str, int *pr_port,
+ const char **pl_ip_str, int *pl_port)
+{
+ if(Curl_cf_is_socket(cf) && cf->ctx) {
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ if(psock)
+ *psock = ctx->sock;
+ if(paddr)
+ *paddr = &ctx->addr;
+ if(pr_ip_str)
+ *pr_ip_str = ctx->r_ip;
+ if(pr_port)
+ *pr_port = ctx->r_port;
+ if(pl_port ||pl_ip_str) {
+ set_local_ip(cf, data);
+ if(pl_ip_str)
+ *pl_ip_str = ctx->l_ip;
+ if(pl_port)
+ *pl_port = ctx->l_port;
+ }
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h
new file mode 100644
index 0000000000..f6eb810a52
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -0,0 +1,192 @@
+#ifndef HEADER_CURL_CF_SOCKET_H
+#define HEADER_CURL_CF_SOCKET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "sockaddr.h"
+
+struct Curl_addrinfo;
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_sockaddr_ex;
+
+/*
+ * The Curl_sockaddr_ex structure is basically 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 addr;
+ struct Curl_sockaddr_storage buff;
+ } _sa_ex_u;
+};
+#define sa_addr _sa_ex_u.addr
+
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
+ * socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket_open(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ int transport,
+ curl_socket_t *sockfd);
+
+int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock);
+
+/*
+ * This function should return TRUE if the socket is to be assumed to
+ * be dead. Most commonly this happens when the server has closed the
+ * connection due to inactivity.
+ */
+bool Curl_socket_is_dead(curl_socket_t sock);
+
+/**
+ * Determine the curl code for a socket connect() == -1 with errno.
+ */
+CURLcode Curl_socket_connect_result(struct Curl_easy *data,
+ const char *ipaddress, int error);
+
+#ifdef USE_WINSOCK
+/* When you run a program that uses the Windows Sockets API, you may
+ experience slow performance when you copy data to a TCP server.
+
+ https://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+*/
+void Curl_sndbufset(curl_socket_t sockfd);
+#else
+#define Curl_sndbufset(y) Curl_nop_stmt
+#endif
+
+/**
+ * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
+ * set the transport used.
+ */
+void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a TCP socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a UDP socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a UNIX socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that keeps a listening socket.
+ */
+CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ curl_socket_t *s);
+
+/**
+ * Replace the listen socket with the accept()ed one.
+ */
+CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ curl_socket_t *s);
+
+/**
+ * Return TRUE iff `cf` is a socket filter.
+ */
+bool Curl_cf_is_socket(struct Curl_cfilter *cf);
+
+/**
+ * Peek at the socket and remote ip/port the socket filter is using.
+ * The filter owns all returned values.
+ * @param psock pointer to hold socket descriptor or NULL
+ * @param paddr pointer to hold addr reference or NULL
+ * @param pr_ip_str pointer to hold remote addr as string or NULL
+ * @param pr_port pointer to hold remote port number or NULL
+ * @param pl_ip_str pointer to hold local addr as string or NULL
+ * @param pl_port pointer to hold local port number or NULL
+ * Returns error if the filter is of invalid type.
+ */
+CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *psock,
+ const struct Curl_sockaddr_ex **paddr,
+ const char **pr_ip_str, int *pr_port,
+ const char **pl_ip_str, int *pl_port);
+
+extern struct Curl_cftype Curl_cft_tcp;
+extern struct Curl_cftype Curl_cft_udp;
+extern struct Curl_cftype Curl_cft_unix;
+extern struct Curl_cftype Curl_cft_tcp_accept;
+
+#endif /* HEADER_CURL_CF_SOCKET_H */
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index bcb33da771..2af0dd8288 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -34,9 +34,6 @@
#include "multiif.h"
#include "progress.h"
#include "warnless.h"
-#include "http_proxy.h"
-#include "socks.h"
-#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -54,89 +51,116 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)data;
}
-CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
-{
- DEBUGASSERT(cf->next);
- return cf->next->cft->setup(cf->next, data, remotehost);
-}
-
-void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
-}
-
-void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
-}
-
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- DEBUGASSERT(cf->next);
cf->connected = FALSE;
- cf->next->cft->close(cf->next, data);
+ if(cf->next)
+ cf->next->cft->close(cf->next, data);
}
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->connect(cf->next, data, blocking, done);
+ CURLcode result;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ if(cf->next) {
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(!result && *done) {
+ cf->connected = TRUE;
+ }
+ return result;
+ }
+ *done = FALSE;
+ return CURLE_FAILED_INIT;
}
void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
const char **phost, const char **pdisplay_host,
int *pport)
{
- DEBUGASSERT(cf->next);
- cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ if(cf->next)
+ cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ else {
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+ }
}
int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->get_select_socks(cf->next, data, socks);
+ return cf->next?
+ cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
}
bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->has_data_pending(cf->next, data);
+ return cf->next?
+ cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->do_send(cf->next, data, buf, len, err);
+ return cf->next?
+ cf->next->cft->do_send(cf->next, data, buf, len, err) :
+ CURLE_RECV_ERROR;
}
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->do_recv(cf->next, data, buf, len, err);
+ return cf->next?
+ cf->next->cft->do_recv(cf->next, data, buf, len, err) :
+ CURLE_SEND_ERROR;
}
-void Curl_conn_cf_discard_all(struct Curl_easy *data,
- struct connectdata *conn, int index)
+bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct Curl_cfilter *cfn, *cf = conn->cfilter[index];
+ return cf->next?
+ cf->next->cft->is_alive(cf->next, data) :
+ FALSE; /* pessimistic in absence of data */
+}
+
+CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ return cf->next?
+ cf->next->cft->keep_alive(cf->next, data) :
+ CURLE_OK;
+}
+
+CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cfn, *cf = *pcf;
if(cf) {
- conn->cfilter[index] = NULL;
+ *pcf = NULL;
while(cf) {
cfn = cf->next;
+ /* prevent destroying filter to mess with its sub-chain, since
+ * we have the reference now and will call destroy on it.
+ */
+ cf->next = NULL;
cf->cft->destroy(cf, data);
free(cf);
cf = cfn;
@@ -144,6 +168,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
}
}
+void Curl_conn_cf_discard_all(struct Curl_easy *data,
+ struct connectdata *conn, int index)
+{
+ Curl_conn_cf_discard_chain(&conn->cfilter[index], data);
+}
+
void Curl_conn_close(struct Curl_easy *data, int index)
{
struct Curl_cfilter *cf;
@@ -160,7 +190,6 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
size_t len, CURLcode *code)
{
struct Curl_cfilter *cf;
- ssize_t nread;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
@@ -169,13 +198,9 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
cf = cf->next;
}
if(cf) {
- nread = cf->cft->do_recv(cf, data, buf, len, code);
- /* DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)"
- "-> %ld, err=%d", data, num, nread, *code));*/
- return nread;
+ return cf->cft->do_recv(cf, data, buf, len, code);
}
- failf(data, "no filter connected, conn=%ld, sockindex=%d",
- data->conn->connection_id, num);
+ failf(data, CMSGI(data->conn, num, "recv: no filter connected"));
*code = CURLE_FAILED_INIT;
return -1;
}
@@ -184,7 +209,6 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
const void *mem, size_t len, CURLcode *code)
{
struct Curl_cfilter *cf;
- ssize_t nwritten;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
@@ -193,13 +217,10 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
cf = cf->next;
}
if(cf) {
- nwritten = cf->cft->do_send(cf, data, mem, len, code);
- /* DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)"
- " -> %ld, err=%d", data, num, len, nwritten, *code));*/
- return nwritten;
+ return cf->cft->do_send(cf, data, mem, len, code);
}
- failf(data, "no filter connected, conn=%ld, sockindex=%d",
- data->conn->connection_id, num);
+ failf(data, CMSGI(data->conn, num, "send: no filter connected"));
+ DEBUGASSERT(0);
*code = CURLE_FAILED_INIT;
return -1;
}
@@ -234,12 +255,31 @@ void Curl_conn_cf_add(struct Curl_easy *data,
DEBUGASSERT(!cf->conn);
DEBUGASSERT(!cf->next);
- DEBUGF(infof(data, CMSGI(conn, index, "cf_add(filter=%s)"),
- cf->cft->name));
cf->next = conn->cfilter[index];
cf->conn = conn;
cf->sockindex = index;
conn->cfilter[index] = cf;
+ DEBUGF(LOG_CF(data, cf, "added"));
+}
+
+void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_cfilter *cf_new)
+{
+ struct Curl_cfilter *tail, **pnext;
+
+ DEBUGASSERT(cf_at);
+ DEBUGASSERT(cf_new);
+ DEBUGASSERT(!cf_new->conn);
+
+ tail = cf_at->next;
+ cf_at->next = cf_new;
+ do {
+ cf_new->conn = cf_at->conn;
+ cf_new->sockindex = cf_at->sockindex;
+ pnext = &cf_new->next;
+ cf_new = cf_new->next;
+ } while(cf_new);
+ *pnext = tail;
}
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -259,97 +299,54 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
free(cf);
}
-ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- return cf->cft->do_send(cf, data, buf, len, err);
+ if(cf)
+ return cf->cft->connect(cf, data, blocking, done);
+ return CURLE_FAILED_INIT;
}
-ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
- char *buf, size_t len, CURLcode *err)
+void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- return cf->cft->do_recv(cf, data, buf, len, err);
+ if(cf)
+ cf->cft->close(cf, data);
}
-CURLcode Curl_conn_setup(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost,
- int ssl_mode)
+int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
{
- struct Curl_cfilter *cf;
- CURLcode result;
-
- DEBUGASSERT(data);
- /* If no filter is set, we have the "default" setup of connection filters.
- * The filter chain from botton to top will be:
- * - SOCKET socket filter for outgoing connection to remotehost
- * if http_proxy tunneling is engaged:
- * - SSL if proxytype is CURLPROXY_HTTPS
- * - HTTP_PROXY_TUNNEL
- * otherwise, if socks_proxy is engaged:
- * - SOCKS_PROXY_TUNNEL
- * - SSL if conn->handler has PROTOPT_SSL
- */
- if(!conn->cfilter[sockindex]) {
- DEBUGF(infof(data, DMSGI(data, sockindex, "setup, init filter chain")));
- result = Curl_conn_socket_set(data, conn, sockindex);
- if(result)
- goto out;
-
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy) {
- result = Curl_conn_socks_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
+ if(cf)
+ return cf->cft->get_select_socks(cf, data, socks);
+ return 0;
+}
- if(conn->bits.httpproxy) {
-#ifdef USE_SSL
- if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
- result = Curl_ssl_cfilter_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* USE_SSL */
-
-#if !defined(CURL_DISABLE_HTTP)
- if(conn->bits.tunnel_proxy) {
- result = Curl_conn_http_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* !CURL_DISABLE_HTTP */
- }
-#endif /* !CURL_DISABLE_PROXY */
-
-#ifdef USE_SSL
- if(ssl_mode == CURL_CF_SSL_ENABLE
- || (ssl_mode != CURL_CF_SSL_DISABLE
- && conn->handler->flags & PROTOPT_SSL)) {
- result = Curl_ssl_cfilter_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#else
- (void)ssl_mode;
-#endif /* USE_SSL */
-
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
- if(data->set.haproxyprotocol) {
- result = Curl_conn_haproxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ if(cf)
+ return cf->cft->has_data_pending(cf, data);
+ return FALSE;
+}
- }
- DEBUGASSERT(conn->cfilter[sockindex]);
- cf = data->conn->cfilter[sockindex];
- result = cf->cft->setup(cf, data, remotehost);
+ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ if(cf)
+ return cf->cft->do_send(cf, data, buf, len, err);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+}
-out:
- return result;
+ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ if(cf)
+ return cf->cft->do_recv(cf, data, buf, len, err);
+ *err = CURLE_RECV_ERROR;
+ return -1;
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
@@ -358,16 +355,26 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
bool *done)
{
struct Curl_cfilter *cf;
- CURLcode result;
+ CURLcode result = CURLE_OK;
DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
DEBUGASSERT(cf);
- result = cf->cft->connect(cf, data, blocking, done);
+ if(!cf)
+ return CURLE_FAILED_INIT;
+
+ *done = cf->connected;
+ if(!*done) {
+ result = cf->cft->connect(cf, data, blocking, done);
+ if(!result && *done) {
+ Curl_conn_ev_update_info(data, data->conn);
+ Curl_conn_ev_report_stats(data, data->conn);
+ data->conn->keepalive = Curl_now();
+ }
+ }
- DEBUGF(infof(data, DMSGI(data, sockindex, "connect(block=%d)-> %d, done=%d"),
- blocking, result, *done));
return result;
}
@@ -394,11 +401,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
return FALSE;
}
-bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
+bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
{
- struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
- (void)data;
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_SSL)
return TRUE;
@@ -408,6 +414,19 @@ bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
return FALSE;
}
+bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ for(; cf; cf = cf->next) {
+ if(cf->cft->flags & CF_TYPE_MULTIPLEX)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT
+ || cf->cft->flags & CF_TYPE_SSL)
+ return FALSE;
+ }
+ return FALSE;
+}
bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
{
@@ -416,8 +435,6 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
(void)data;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
- if(Curl_recv_has_postponed_data(data->conn, sockindex))
- return TRUE;
cf = data->conn->cfilter[sockindex];
while(cf && !cf->connected) {
@@ -437,46 +454,16 @@ int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
+
+ /* if the next one is not yet connected, that's the one we want */
+ while(cf && cf->next && !cf->next->connected)
+ cf = cf->next;
if(cf) {
return cf->cft->get_select_socks(cf, data, socks);
}
return GETSOCK_BLANK;
}
-void Curl_conn_attach_data(struct connectdata *conn,
- struct Curl_easy *data)
-{
- size_t i;
- struct Curl_cfilter *cf;
-
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
- cf = conn->cfilter[i];
- if(cf) {
- while(cf) {
- cf->cft->attach_data(cf, data);
- cf = cf->next;
- }
- }
- }
-}
-
-void Curl_conn_detach_data(struct connectdata *conn,
- struct Curl_easy *data)
-{
- size_t i;
- struct Curl_cfilter *cf;
-
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
- cf = conn->cfilter[i];
- if(cf) {
- while(cf) {
- cf->cft->detach_data(cf, data);
- cf = cf->next;
- }
- }
- }
-}
-
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport)
@@ -491,7 +478,7 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
else {
/* Some filter ask during shutdown for this, mainly for debugging
* purposes. We hand out the defaults, however this is not always
- * accurate, as the connction might be tunneled, etc. But all that
+ * accurate, as the connection might be tunneled, etc. But all that
* state is already gone here. */
*phost = data->conn->host.name;
*pdisplay_host = data->conn->host.dispname;
@@ -499,4 +486,158 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
}
}
+CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ (void)cf;
+ (void)data;
+ (void)event;
+ (void)arg1;
+ (void)arg2;
+ return CURLE_OK;
+}
+
+CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+
+ for(; cf; cf = cf->next) {
+ if(Curl_cf_def_cntrl == cf->cft->cntrl)
+ continue;
+ result = cf->cft->cntrl(cf, data, event, arg1, arg2);
+ if(!ignore_result && result)
+ break;
+ }
+ return result;
+}
+
+curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ curl_socket_t sock;
+ if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock))
+ return sock;
+ return CURL_SOCKET_BAD;
+}
+
+curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+ /* if the top filter has not connected, ask it (and its sub-filters)
+ * for the socket. Otherwise conn->sock[sockindex] should have it.
+ */
+ if(cf && !cf->connected)
+ return Curl_conn_cf_get_socket(cf, data);
+ return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
+}
+
+static CURLcode cf_cntrl_all(struct connectdata *conn,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+ size_t i;
+
+ for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
+ event, arg1, arg2);
+ if(!ignore_result && result)
+ break;
+ }
+ return result;
+}
+
+void Curl_conn_ev_data_attach(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL);
+}
+
+void Curl_conn_ev_data_detach(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL);
+}
+
+CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_SETUP, 0, NULL);
+}
+
+CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_IDLE, 0, NULL);
+}
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is donw with sending data (e.g. has uploaded everything).
+ */
+void Curl_conn_ev_data_done_send(struct Curl_easy *data)
+{
+ cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL);
+}
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is finished - eventually premature, e.g. before being complete.
+ */
+void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature)
+{
+ cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL);
+}
+
+CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_PAUSE, do_pause, NULL);
+}
+
+void Curl_conn_ev_update_info(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+}
+
+void Curl_conn_ev_report_stats(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_REPORT_STATS, 0, NULL);
+}
+
+bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn)
+{
+ struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+ return cf && !cf->conn->bits.close && cf->cft->is_alive(cf, data);
+}
+
+CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ return cf? cf->cft->keep_alive(cf, data) : CURLE_OK;
+}
+
+size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode result;
+ int n = 0;
+
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
+ &n, NULL) : CURLE_UNKNOWN_OPTION;
+ return (result || n <= 0)? 1 : (size_t)n;
+}
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h
index 4b81b42e6b..94dc53f813 100644
--- a/Utilities/cmcurl/lib/cfilters.h
+++ b/Utilities/cmcurl/lib/cfilters.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,11 +36,6 @@ struct connectdata;
typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf,
struct Curl_easy *data);
-/* Setup the connection for `data`, using destination `remotehost`.
- */
-typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost);
typedef void Curl_cft_close(struct Curl_cfilter *cf,
struct Curl_easy *data);
@@ -89,29 +84,89 @@ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf,
size_t len, /* amount to read */
CURLcode *err); /* error to return */
-typedef void Curl_cft_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-typedef void Curl_cft_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
+typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
/**
- * The easy handle `data` is being detached (no longer served)
- * by connection `conn`. All filters are informed to release any resources
- * related to `data`.
- * Note: there may be several `data` attached to a connection at the same
- * time.
+ * Events/controls for connection filters, their arguments and
+ * return code handling. Filter callbacks are invoked "top down".
+ * Return code handling:
+ * "first fail" meaning that the first filter returning != CURLE_OK, will
+ * abort further event distribution and determine the result.
+ * "ignored" meaning return values are ignored and the event is distributed
+ * to all filters in the chain. Overall result is always CURLE_OK.
*/
-void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data);
+/* data event arg1 arg2 return */
+#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */
+#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */
+#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */
+#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */
+#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */
+#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */
+#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */
+/* update conn info at connection and data */
+#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */
+/* report conn statistics (timers) for connection and data */
+#define CF_CTRL_CONN_REPORT_STATS (256+1) /* 0 NULL ignored */
+/**
+ * Handle event/control for the filter.
+ * Implementations MUST NOT chain calls to cf->next.
+ */
+typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2);
+
+
+/**
+ * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain.
+ * - MAX_CONCURRENT: the maximum number of parallel transfers the filter
+ * chain expects to handle at the same time.
+ * default: 1 if no filter overrides.
+ * - CONNECT_REPLY_MS: milliseconds until the first indication of a server
+ * response was received on a connect. For TCP, this
+ * reflects the time until the socket connected. On UDP
+ * this gives the time the first bytes from the server
+ * were received.
+ * -1 if not determined yet.
+ * - CF_QUERY_SOCKET: the socket used by the filter chain
+ */
+/* query res1 res2 */
+#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
+#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */
+#define CF_QUERY_SOCKET 3 /* - curl_socket_t */
+
+/**
+ * Query the cfilter for properties. Filters ignorant of a query will
+ * pass it "down" the filter chain.
+ */
+typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2);
+
+/**
+ * Type flags for connection filters. A filter can have none, one or
+ * many of those. Use to evaluate state/capabilities of a filter chain.
+ *
+ * CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like
+ * a CONNECT tunnel, a UNIX domain socket, a QUIC
+ * connection, etc.
+ * CF_TYPE_SSL: provide SSL/TLS
+ * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
+ */
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
+#define CF_TYPE_MULTIPLEX (1 << 2)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
const char *name; /* name of the filter type */
- long flags; /* flags of filter type */
+ int flags; /* flags of filter type */
+ int log_level; /* log level for such filters */
Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
- Curl_cft_setup *setup; /* setup for a connection */
Curl_cft_connect *connect; /* establish connection */
Curl_cft_close *close; /* close conn */
Curl_cft_get_host *get_host; /* host filter talks to */
@@ -119,8 +174,10 @@ struct Curl_cftype {
Curl_cft_data_pending *has_data_pending;/* conn has data pending */
Curl_cft_send *do_send; /* send data */
Curl_cft_recv *do_recv; /* receive data */
- Curl_cft_attach_data *attach_data; /* data is being handled here */
- Curl_cft_detach_data *detach_data; /* data is no longer handled here */
+ Curl_cft_cntrl *cntrl; /* events/control */
+ Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */
+ Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */
+ Curl_cft_query *query; /* query filter chain */
};
/* A connection filter instance, e.g. registered at a connection */
@@ -129,7 +186,7 @@ struct Curl_cfilter {
struct Curl_cfilter *next; /* next filter in chain */
void *ctx; /* filter type specific settings */
struct connectdata *conn; /* the connection this filter belongs to */
- int sockindex; /* TODO: like to get rid off this */
+ int sockindex; /* the index the filter is installed at */
BIT(connected); /* != 0 iff this filter is connected */
};
@@ -139,9 +196,6 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
/* Default implementations for the type functions, implementing pass-through
* the filter chain. */
-CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost);
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -158,16 +212,22 @@ ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
-void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
+CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2);
+bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2);
/**
* Create a new filter instance, unattached to the filter chain.
* Use Curl_conn_cf_add() to add it to the chain.
* @param pcf on success holds the created instance
- * @parm cft the filter type
+ * @param cft the filter type
* @param ctx the type specific context to use
*/
CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
@@ -176,7 +236,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
/**
* Add a filter instance to the `sockindex` filter chain at connection
- * `data->conn`. The filter must not already be attached. It is inserted at
+ * `conn`. The filter must not already be attached. It is inserted at
* the start of the chain (top).
*/
void Curl_conn_cf_add(struct Curl_easy *data,
@@ -185,11 +245,11 @@ void Curl_conn_cf_add(struct Curl_easy *data,
struct Curl_cfilter *cf);
/**
- * Remove and destroy all filters at chain `sockindex` on connection `conn`.
+ * Insert a filter (chain) after `cf_at`.
+ * `cf_new` must not already be attached.
*/
-void Curl_conn_cf_discard_all(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex);
+void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_cfilter *cf_new);
/**
* Discard, e.g. remove and destroy a specific filter instance.
@@ -198,29 +258,51 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
*/
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data);
+/**
+ * Discard all cfilters starting with `*pcf` and clearing it afterwards.
+ */
+void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
+ struct Curl_easy *data);
+
+/**
+ * Remove and destroy all filters at chain `sockindex` on connection `conn`.
+ */
+void Curl_conn_cf_discard_all(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done);
+void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
+int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks);
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
+CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2);
+
+/**
+ * Get the socket used by the filter chain starting at `cf`.
+ * Returns CURL_SOCKET_BAD if not available.
+ */
+curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
#define CURL_CF_SSL_DEFAULT -1
#define CURL_CF_SSL_DISABLE 0
#define CURL_CF_SSL_ENABLE 1
/**
- * Setup the filter chain at `sockindex` in connection `conn`, invoking
- * the instance `setup(remotehost)` methods. If no filter chain is
- * installed yet, inspects the configuration in `data` to install a
- * suitable filter chain.
- */
-CURLcode Curl_conn_setup(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost,
- 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.
@@ -248,7 +330,12 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
* (or will be once connected). This will return FALSE, if SSL
* is only used in proxying and not for the tunnel itself.
*/
-bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex);
+bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
+
+/**
+ * Connection provides multiplexing of easy handles at `socketindex`.
+ */
+bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
/**
* Close the filter chain at `sockindex` for connection `data->conn`.
@@ -264,6 +351,12 @@ bool Curl_conn_data_pending(struct Curl_easy *data,
int sockindex);
/**
+ * Return the socket used on data's connection for the index.
+ * Returns CURL_SOCKET_BAD if not available.
+ */
+curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
+
+/**
* Get any select fd flags and the socket filters at chain `sockindex`
* at connection `conn` might be waiting for.
*/
@@ -289,13 +382,12 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t len, CURLcode *code);
/**
- * The easy handle `data` is being attached (served) by connection `conn`.
- * All filters are informed to adapt to handling `data`.
- * Note: there may be several `data` attached to a connection at the same
- * time.
+ * The easy handle `data` is being attached to `conn`. This does
+ * not mean that data will actually do a transfer. Attachment is
+ * also used for temporary actions on the connection.
*/
-void Curl_conn_attach_data(struct connectdata *conn,
- struct Curl_easy *data);
+void Curl_conn_ev_data_attach(struct connectdata *conn,
+ struct Curl_easy *data);
/**
* The easy handle `data` is being detached (no longer served)
@@ -304,12 +396,141 @@ void Curl_conn_attach_data(struct connectdata *conn,
* Note: there may be several `data` attached to a connection at the same
* time.
*/
-void Curl_conn_detach_data(struct connectdata *conn,
- struct Curl_easy *data);
+void Curl_conn_ev_data_detach(struct connectdata *conn,
+ struct Curl_easy *data);
+
+/**
+ * Notify connection filters that they need to setup data for
+ * a transfer.
+ */
+CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that now would be a good time to
+ * perform any idle, e.g. time related, actions.
+ */
+CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is donw with sending data (e.g. has uploaded everything).
+ */
+void Curl_conn_ev_data_done_send(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is finished - eventually premature, e.g. before being complete.
+ */
+void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature);
+
+/**
+ * Notify connection filters that the transfer of data is paused/unpaused.
+ */
+CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause);
+
+/**
+ * Inform connection filters to update their info in `conn`.
+ */
+void Curl_conn_ev_update_info(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
+ * Inform connection filters to report statistics.
+ */
+void Curl_conn_ev_report_stats(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
+ * Check if FIRSTSOCKET's cfilter chain deems connection alive.
+ */
+bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn);
+
+/**
+ * Try to upkeep the connection filters at sockindex.
+ */
+CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport);
+/**
+ * Get the maximum number of parallel transfers the connection
+ * expects to be able to handle at `sockindex`.
+ */
+size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+
+/**
+ * Types and macros used to keep the current easy handle in filter calls,
+ * allowing for nested invocations. See #10336.
+ *
+ * `cf_call_data` is intended to be a member of the cfilter's `ctx` type.
+ * A filter defines the macro `CF_CTX_CALL_DATA` to give access to that.
+ *
+ * With all values 0, the default, this indicates that there is no cfilter
+ * call with `data` ongoing.
+ * Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local
+ * variable and sets the `data` given, incrementing the `depth` counter.
+ *
+ * Macro `CF_DATA_RESTORE` restores the old values from the local variable,
+ * while checking that `depth` values are as expected (debug build), catching
+ * cases where a "lower" RESTORE was not called.
+ *
+ * Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current
+ * invocation.
+ */
+struct cf_call_data {
+ struct Curl_easy *data;
+#ifdef DEBUGBUILD
+ int depth;
+#endif
+};
+
+/**
+ * define to access the `struct cf_call_data for a cfilter. Normally
+ * a member in the cfilter's `ctx`.
+ *
+ * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance
+*/
+
+#ifdef DEBUGBUILD
+
+#define CF_DATA_SAVE(save, cf, data) \
+ do { \
+ (save) = CF_CTX_CALL_DATA(cf); \
+ DEBUGASSERT((save).data == NULL || (save).depth > 0); \
+ CF_CTX_CALL_DATA(cf).depth++; \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ } while(0)
+
+#define CF_DATA_RESTORE(cf, save) \
+ do { \
+ DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \
+ DEBUGASSERT((save).data == NULL || (save).depth > 0); \
+ CF_CTX_CALL_DATA(cf) = (save); \
+ } while(0)
+
+#else /* DEBUGBUILD */
+
+#define CF_DATA_SAVE(save, cf, data) \
+ do { \
+ (save) = CF_CTX_CALL_DATA(cf); \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ } while(0)
+
+#define CF_DATA_RESTORE(cf, save) \
+ do { \
+ CF_CTX_CALL_DATA(cf) = (save); \
+ } while(0)
+
+#endif /* !DEBUGBUILD */
+
+#define CF_DATA_CURRENT(cf) \
+ ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL)
#endif /* HEADER_CURL_CFILTERS_H */
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index a557ac6dc9..c21b96ca83 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h
index 94664bc357..959767d596 100644
--- a/Utilities/cmcurl/lib/conncache.h
+++ b/Utilities/cmcurl/lib/conncache.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index af0413836b..993a7f919b 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,106 +59,30 @@
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
+#include "cf-http.h"
+#include "cf-socket.h"
#include "select.h"
#include "url.h" /* for Curl_safefree() */
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "inet_ntop.h"
#include "inet_pton.h"
-#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
+#include "vtls/vtls.h" /* for vtsl cfilters */
#include "progress.h"
#include "warnless.h"
#include "conncache.h"
#include "multihandle.h"
#include "share.h"
#include "version_win32.h"
-#include "quic.h"
+#include "vquic/vquic.h" /* for quic cfilters */
+#include "http_proxy.h"
+#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-static bool verifyconnect(curl_socket_t sockfd, int *error);
-
-#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
-/* DragonFlyBSD and Windows use millisecond units */
-#define KEEPALIVE_FACTOR(x) (x *= 1000)
-#else
-#define KEEPALIVE_FACTOR(x)
-#endif
-
-#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
-#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
-
-struct tcp_keepalive {
- u_long onoff;
- u_long keepalivetime;
- u_long keepaliveinterval;
-};
-#endif
-
-static void
-tcpkeepalive(struct Curl_easy *data,
- curl_socket_t sockfd)
-{
- int optval = data->set.tcp_keepalive?1:0;
-
- /* only set IDLE and INTVL if setting KEEPALIVE is successful */
- if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
- }
- else {
-#if defined(SIO_KEEPALIVE_VALS)
- struct tcp_keepalive vals;
- DWORD dummy;
- vals.onoff = 1;
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- vals.keepalivetime = optval;
- optval = curlx_sltosi(data->set.tcp_keepintvl);
- KEEPALIVE_FACTOR(optval);
- vals.keepaliveinterval = optval;
- if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
- NULL, 0, &dummy, NULL, NULL) != 0) {
- infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
- (int)sockfd, WSAGetLastError());
- }
-#else
-#ifdef TCP_KEEPIDLE
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
- }
-#elif defined(TCP_KEEPALIVE)
- /* Mac OS X style */
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
- }
-#endif
-#ifdef TCP_KEEPINTVL
- optval = curlx_sltosi(data->set.tcp_keepintvl);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
- }
-#endif
-#endif
- }
-}
-
-static CURLcode
-singleipconnect(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai, /* start connecting to this */
- int tempindex); /* 0 or 1 among the temp ones */
/*
* Curl_timeleft() returns the amount of milliseconds left allowed for the
@@ -230,387 +154,6 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
return timeout_ms;
}
-static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd, int af, unsigned int scope)
-{
- struct Curl_sockaddr_storage sa;
- struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
- curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
- struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
-#endif
-
- struct Curl_dns_entry *h = NULL;
- unsigned short port = data->set.localport; /* use this port number, 0 for
- "random" */
- /* how many port numbers to try to bind to, increasing one at a time */
- int portnum = data->set.localportrange;
- const char *dev = data->set.str[STRING_DEVICE];
- int error;
-#ifdef IP_BIND_ADDRESS_NO_PORT
- int on = 1;
-#endif
-#ifndef ENABLE_IPV6
- (void)scope;
-#endif
-
- /*************************************************************
- * Select device to bind socket to
- *************************************************************/
- if(!dev && !port)
- /* no local kind of binding was requested */
- return CURLE_OK;
-
- memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
-
- if(dev && (strlen(dev)<255) ) {
- char myhost[256] = "";
- int done = 0; /* -1 for error, 1 for address found */
- bool is_interface = FALSE;
- bool is_host = FALSE;
- static const char *if_prefix = "if!";
- static const char *host_prefix = "host!";
-
- if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
- dev += strlen(if_prefix);
- is_interface = TRUE;
- }
- else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
- dev += strlen(host_prefix);
- is_host = TRUE;
- }
-
- /* interface */
- if(!is_host) {
-#ifdef SO_BINDTODEVICE
- /* I am not sure any other OSs than Linux that provide this feature,
- * and at the least I cannot test. --Ben
- *
- * This feature allows one to tightly bind the local socket to a
- * particular interface. This will force even requests to other
- * local interfaces to go out the external interface.
- *
- *
- * Only bind to the interface when specified as interface, not just
- * as a hostname or ip address.
- *
- * interface might be a VRF, eg: vrf-blue, which means it cannot be
- * converted to an IP address and would fail Curl_if2ip. Simply try
- * to use it straight away.
- */
- if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
- dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
- /* This is typically "errno 1, error: Operation not permitted" if
- * you're not running as root or another suitable privileged
- * user.
- * If it succeeds it means the parameter was a valid interface and
- * not an IP address. Return immediately.
- */
- return CURLE_OK;
- }
-#endif
-
- switch(Curl_if2ip(af,
-#ifdef ENABLE_IPV6
- scope, conn->scope_id,
-#endif
- dev, myhost, sizeof(myhost))) {
- case IF2IP_NOT_FOUND:
- if(is_interface) {
- /* Do not fall back to treating it as a host name */
- failf(data, "Couldn't bind to interface '%s'", dev);
- return CURLE_INTERFACE_FAILED;
- }
- break;
- case IF2IP_AF_NOT_SUPPORTED:
- /* Signal the caller to try another address family if available */
- return CURLE_UNSUPPORTED_PROTOCOL;
- case IF2IP_FOUND:
- is_interface = TRUE;
- /*
- * We now have the numerical IP address in the 'myhost' buffer
- */
- infof(data, "Local Interface %s is ip %s using address family %i",
- dev, myhost, af);
- done = 1;
- break;
- }
- }
- if(!is_interface) {
- /*
- * This was not an interface, resolve the name as a host name
- * or IP number
- *
- * Temporarily force name resolution to use only the address type
- * of the connection. The resolve functions should really be changed
- * to take a type parameter instead.
- */
- unsigned char ipver = conn->ip_version;
- int rc;
-
- if(af == AF_INET)
- conn->ip_version = CURL_IPRESOLVE_V4;
-#ifdef ENABLE_IPV6
- else if(af == AF_INET6)
- conn->ip_version = CURL_IPRESOLVE_V6;
-#endif
-
- rc = Curl_resolv(data, dev, 0, FALSE, &h);
- if(rc == CURLRESOLV_PENDING)
- (void)Curl_resolver_wait_resolv(data, &h);
- conn->ip_version = ipver;
-
- if(h) {
- /* 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",
- dev, af, myhost, h->addr->ai_family);
- Curl_resolv_unlock(data, h);
- if(af != h->addr->ai_family) {
- /* bad IP version combo, signal the caller to try another address
- family if available */
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
- done = 1;
- }
- else {
- /*
- * provided dev was no interface (or interfaces are not supported
- * e.g. solaris) no ip address and no domain we fail here
- */
- done = -1;
- }
- }
-
- if(done > 0) {
-#ifdef ENABLE_IPV6
- /* IPv6 address */
- if(af == AF_INET6) {
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
- char *scope_ptr = strchr(myhost, '%');
- if(scope_ptr)
- *(scope_ptr++) = '\0';
-#endif
- if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
- si6->sin6_family = AF_INET6;
- si6->sin6_port = htons(port);
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
- if(scope_ptr) {
- /* The "myhost" string either comes from Curl_if2ip or from
- Curl_printable_address. The latter returns only numeric scope
- IDs and the former returns none at all. So the scope ID, if
- present, is known to be numeric */
- unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
- if(scope_id > UINT_MAX)
- return CURLE_UNSUPPORTED_PROTOCOL;
-
- si6->sin6_scope_id = (unsigned int)scope_id;
- }
-#endif
- }
- sizeof_sa = sizeof(struct sockaddr_in6);
- }
- else
-#endif
- /* IPv4 address */
- if((af == AF_INET) &&
- (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
- si4->sin_family = AF_INET;
- si4->sin_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in);
- }
- }
-
- if(done < 1) {
- /* errorbuf is set false so failf will overwrite any message already in
- the error buffer, so the user receives this error message instead of a
- generic resolve error. */
- data->state.errorbuf = FALSE;
- failf(data, "Couldn't bind to '%s'", dev);
- return CURLE_INTERFACE_FAILED;
- }
- }
- else {
- /* no device was given, prepare sa to match af's needs */
-#ifdef ENABLE_IPV6
- if(af == AF_INET6) {
- si6->sin6_family = AF_INET6;
- si6->sin6_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in6);
- }
- else
-#endif
- if(af == AF_INET) {
- si4->sin_family = AF_INET;
- si4->sin_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in);
- }
- }
-#ifdef IP_BIND_ADDRESS_NO_PORT
- (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
-#endif
- for(;;) {
- if(bind(sockfd, sock, sizeof_sa) >= 0) {
- /* we succeeded to bind */
- struct Curl_sockaddr_storage add;
- curl_socklen_t size = sizeof(add);
- memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
- if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
- char buffer[STRERROR_LEN];
- data->state.os_errno = error = SOCKERRNO;
- failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return CURLE_INTERFACE_FAILED;
- }
- infof(data, "Local port: %hu", port);
- conn->bits.bound = TRUE;
- return CURLE_OK;
- }
-
- if(--portnum > 0) {
- port++; /* try next port */
- if(port == 0)
- break;
- infof(data, "Bind to local port %hu failed, trying next", port - 1);
- /* We re-use/clobber the port variable here below */
- if(sock->sa_family == AF_INET)
- si4->sin_port = ntohs(port);
-#ifdef ENABLE_IPV6
- else
- si6->sin6_port = ntohs(port);
-#endif
- }
- else
- break;
- }
- {
- char buffer[STRERROR_LEN];
- data->state.os_errno = error = SOCKERRNO;
- failf(data, "bind failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- }
-
- return CURLE_INTERFACE_FAILED;
-}
-
-/*
- * verifyconnect() returns TRUE if the connect really has happened.
- */
-static bool verifyconnect(curl_socket_t sockfd, int *error)
-{
- bool rc = TRUE;
-#ifdef SO_ERROR
- int err = 0;
- curl_socklen_t errSize = sizeof(err);
-
-#ifdef WIN32
- /*
- * In October 2003 we effectively nullified this function on Windows due to
- * problems with it using all CPU in multi-threaded cases.
- *
- * In May 2004, we bring it back to offer more info back on connect failures.
- * Gisle Vanem could reproduce the former problems with this function, but
- * could avoid them by adding this SleepEx() call below:
- *
- * "I don't have Rational Quantify, but the hint from his post was
- * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
- * just Sleep(0) would be enough?) would release whatever
- * mutex/critical-section the ntdll call is waiting on.
- *
- * Someone got to verify this on Win-NT 4.0, 2000."
- */
-
-#ifdef _WIN32_WCE
- Sleep(0);
-#else
- SleepEx(0, FALSE);
-#endif
-
-#endif
-
- if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
- err = SOCKERRNO;
-#ifdef _WIN32_WCE
- /* Old WinCE versions don't support SO_ERROR */
- if(WSAENOPROTOOPT == err) {
- SET_SOCKERRNO(0);
- err = 0;
- }
-#endif
-#if defined(EBADIOCTL) && defined(__minix)
- /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
- if(EBADIOCTL == err) {
- SET_SOCKERRNO(0);
- err = 0;
- }
-#endif
- if((0 == err) || (EISCONN == err))
- /* we are connected, awesome! */
- rc = TRUE;
- else
- /* This wasn't a successful connect */
- rc = FALSE;
- if(error)
- *error = err;
-#else
- (void)sockfd;
- if(error)
- *error = SOCKERRNO;
-#endif
- return rc;
-}
-
-/* update tempaddr[tempindex] (to the next entry), makes sure to stick
- to the correct family */
-static struct Curl_addrinfo *ainext(struct connectdata *conn,
- int tempindex,
- bool next) /* use next entry? */
-{
- struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
- if(ai && next)
- ai = ai->ai_next;
- while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
- ai = ai->ai_next;
- conn->tempaddr[tempindex] = ai;
- return ai;
-}
-
-/* Used within the multi interface. Try next IP address, returns error if no
- more address exists or error */
-static CURLcode trynextip(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- int tempindex)
-{
- CURLcode result = CURLE_COULDNT_CONNECT;
-
- /* First clean up after the failed socket.
- Don't close it yet to ensure that the next IP's socket gets a different
- file descriptor, which can prevent bugs when the curl_multi_socket_action
- interface is used with certain select() replacements such as kqueue. */
- curl_socket_t fd_to_close = conn->tempsock[tempindex];
- conn->tempsock[tempindex] = CURL_SOCKET_BAD;
-
- if(sockindex == FIRSTSOCKET) {
- struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
-
- while(ai) {
- result = singleipconnect(data, conn, ai, tempindex);
- if(result == CURLE_COULDNT_CONNECT) {
- ai = ainext(conn, tempindex, TRUE);
- continue;
- }
- break;
- }
- }
-
- if(fd_to_close != CURL_SOCKET_BAD)
- Curl_closesocket(data, conn, fd_to_close);
-
- return result;
-}
-
/* Copies connection info into the transfer handle to make it available when
the transfer handle is no longer associated with the connection. */
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
@@ -629,6 +172,28 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
data->info.conn_local_port = local_port;
}
+static const struct Curl_addrinfo *
+addr_first_match(const struct Curl_addrinfo *addr, int family)
+{
+ while(addr) {
+ if(addr->ai_family == family)
+ return addr;
+ addr = addr->ai_next;
+ }
+ return NULL;
+}
+
+static const struct Curl_addrinfo *
+addr_next_match(const struct Curl_addrinfo *addr, int family)
+{
+ while(addr && addr->ai_next) {
+ addr = addr->ai_next;
+ if(addr->ai_family == family)
+ return addr;
+ }
+ return NULL;
+}
+
/* retrieves ip address and port from a sockaddr structure.
note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
@@ -686,623 +251,493 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
return FALSE;
}
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_conninfo_remote(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t sockfd)
-{
-#ifdef HAVE_GETPEERNAME
- char buffer[STRERROR_LEN];
- struct Curl_sockaddr_storage ssrem;
- curl_socklen_t plen;
- int port;
- plen = sizeof(struct Curl_sockaddr_storage);
- memset(&ssrem, 0, sizeof(ssrem));
- if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
- int error = SOCKERRNO;
- failf(data, "getpeername() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return;
- }
- if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
- conn->primary_ip, &port)) {
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- return;
- }
-#else
- (void)data;
- (void)conn;
- (void)sockfd;
-#endif
-}
+struct connfind {
+ long id_tofind;
+ struct connectdata *found;
+};
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
- char *local_ip, int *local_port)
+static int conn_is_conn(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
{
-#ifdef HAVE_GETSOCKNAME
- char buffer[STRERROR_LEN];
- struct Curl_sockaddr_storage ssloc;
- curl_socklen_t slen;
- slen = sizeof(struct Curl_sockaddr_storage);
- memset(&ssloc, 0, sizeof(ssloc));
- if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
- int error = SOCKERRNO;
- failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return;
- }
- if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
- local_ip, local_port)) {
- failf(data, "ssloc inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- return;
- }
-#else
+ struct connfind *f = (struct connfind *)param;
(void)data;
- (void)sockfd;
- (void)local_ip;
- (void)local_port;
-#endif
-}
-
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd)
-{
- /* 'local_ip' and 'local_port' get filled with local's numerical
- ip address and port number whenever an outgoing connection is
- **established** from the primary socket to a remote address. */
- char local_ip[MAX_IPADR_LEN] = "";
- int local_port = -1;
-
- if(!conn->bits.reuse &&
- (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen))
- Curl_conninfo_remote(data, conn, sockfd);
- Curl_conninfo_local(data, sockfd, local_ip, &local_port);
-
- /* persist connection info in session handle */
- Curl_persistconninfo(data, conn, local_ip, local_port);
-}
-
-/*
- * post_connect() is called after a successful connect to the peer
- */
-static void post_connect(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
-{
- Curl_updateconninfo(data, conn, conn->sock[sockindex]);
- Curl_verboseconnect(data, conn);
- data->info.numconnects++; /* to track the number of connections made */
+ if(conn->connection_id == f->id_tofind) {
+ f->found = conn;
+ return 1;
+ }
+ return 0;
}
/*
- * is_connected() checks if the socket has connected.
+ * Used to extract socket and connectdata struct for the most recent
+ * transfer on the given Curl_easy.
+ *
+ * The returned socket will be CURL_SOCKET_BAD in case of failure!
*/
-static CURLcode is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected)
+curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
+ struct connectdata **connp)
{
- CURLcode result = CURLE_OK;
- timediff_t allow;
- int error = 0;
- struct curltime now;
- int rc = 0;
- int i;
-
- DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
-
- *connected = FALSE; /* a very negative world view is best */
-
- now = Curl_now();
-
- /* Check if any of the conn->tempsock we use for establishing connections
- * succeeded and, if so, close any ongoing other ones.
- * Transfer the successful conn->tempsock to conn->sock[sockindex]
- * and set conn->tempsock to CURL_SOCKET_BAD.
- * If transport is QUIC, we need to shutdown the ongoing 'other'
- * connect attempts in a QUIC appropriate way. */
- for(i = 0; i<2; i++) {
- const int other = i ^ 1;
- if(conn->tempsock[i] == CURL_SOCKET_BAD)
- continue;
- error = 0;
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- result = Curl_quic_is_connected(data, conn, i, connected);
- if(!result && *connected) {
- /* use this socket from now on */
- conn->sock[sockindex] = conn->tempsock[i];
- conn->ip_addr = conn->tempaddr[i];
- conn->tempsock[i] = CURL_SOCKET_BAD;
- post_connect(data, conn, sockindex);
- connkeep(conn, "HTTP/3 default");
- if(conn->tempsock[other] != CURL_SOCKET_BAD)
- Curl_quic_disconnect(data, conn, other);
- return CURLE_OK;
- }
- /* When a QUIC connect attempt fails, the better error explanation is in
- 'result' and not in errno */
- if(result) {
- conn->tempsock[i] = CURL_SOCKET_BAD;
- error = SOCKERRNO;
- }
- }
- else
-#endif
- {
-#ifdef mpeix
- /* Call this function once now, and ignore the results. We do this to
- "clear" the error state on the socket so that we can later read it
- reliably. This is reported necessary on the MPE/iX operating
- system. */
- (void)verifyconnect(conn->tempsock[i], NULL);
-#endif
+ DEBUGASSERT(data);
- /* check socket for connect */
- rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
- }
+ /* this works for an easy handle:
+ * - that has been used for curl_easy_perform()
+ * - that is associated with a multi handle, and whose connection
+ * was detached with CURLOPT_CONNECT_ONLY
+ */
+ if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
+ struct connectdata *c;
+ struct connfind find;
+ find.id_tofind = data->state.lastconnect_id;
+ find.found = NULL;
- if(rc == 0) { /* no connection yet */
- if(Curl_timediff(now, conn->connecttime) >=
- conn->timeoutms_per_addr[i]) {
- infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
- "ms connect time, move on!", conn->timeoutms_per_addr[i]);
- error = ETIMEDOUT;
- }
+ Curl_conncache_foreach(data,
+ data->share && (data->share->specifier
+ & (1<< CURL_LOCK_DATA_CONNECT))?
+ &data->share->conn_cache:
+ data->multi_easy?
+ &data->multi_easy->conn_cache:
+ &data->multi->conn_cache, &find, conn_is_conn);
- /* should we try another protocol family? */
- if(i == 0 && !conn->bits.parallel_connect &&
- (Curl_timediff(now, conn->connecttime) >=
- data->set.happy_eyeballs_timeout)) {
- conn->bits.parallel_connect = TRUE; /* starting now */
- trynextip(data, conn, sockindex, 1);
- }
+ if(!find.found) {
+ data->state.lastconnect_id = -1;
+ return CURL_SOCKET_BAD;
}
- else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
- if(verifyconnect(conn->tempsock[i], &error)) {
- /* we are connected with TCP, awesome! */
-
- /* use this socket from now on */
- conn->sock[sockindex] = conn->tempsock[i];
- conn->ip_addr = conn->tempaddr[i];
- conn->tempsock[i] = CURL_SOCKET_BAD;
-#ifdef ENABLE_IPV6
- conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
-#endif
- /* close the other socket, if open */
- if(conn->tempsock[other] != CURL_SOCKET_BAD) {
- Curl_closesocket(data, conn, conn->tempsock[other]);
- conn->tempsock[other] = CURL_SOCKET_BAD;
- }
-
- *connected = TRUE;
- return CURLE_OK;
- }
- }
- else if(rc & CURL_CSELECT_ERR) {
- (void)verifyconnect(conn->tempsock[i], &error);
- }
+ c = find.found;
+ if(connp)
+ /* only store this if the caller cares for it */
+ *connp = c;
+ return c->sock[FIRSTSOCKET];
+ }
+ return CURL_SOCKET_BAD;
+}
- /*
- * The connection failed here, we should attempt to connect to the "next
- * address" for the given host. But first remember the latest error.
- */
- if(error) {
- data->state.os_errno = error;
- SET_SOCKERRNO(error);
- if(conn->tempaddr[i]) {
- CURLcode status;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
- char ipaddress[MAX_IPADR_LEN];
- char buffer[STRERROR_LEN];
- Curl_printable_address(conn->tempaddr[i], ipaddress,
- sizeof(ipaddress));
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- infof(data, "connect to %s port %u failed: %s",
- ipaddress, conn->port, curl_easy_strerror(result));
- }
- else
+/*
+ * Curl_conncontrol() marks streams or connection for closure.
+ */
+void Curl_conncontrol(struct connectdata *conn,
+ int ctrl /* see defines in header */
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ , const char *reason
#endif
- infof(data, "connect to %s port %u failed: %s",
- ipaddress, conn->port,
- Curl_strerror(error, buffer, sizeof(buffer)));
+ )
+{
+ /* close if a connection, or a stream that isn't multiplexed. */
+ /* This function will be called both before and after this connection is
+ associated with a transfer. */
+ bool closeit, is_multiplex;
+ DEBUGASSERT(conn);
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void)reason; /* useful for debugging */
#endif
-
- allow = Curl_timeleft(data, &now, TRUE);
- conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
- allow : allow / 2;
- ainext(conn, i, TRUE);
- status = trynextip(data, conn, sockindex, i);
- if((status != CURLE_COULDNT_CONNECT) ||
- conn->tempsock[other] == CURL_SOCKET_BAD) {
- /* the last attempt failed and no other sockets remain open */
- if(!result)
- result = status;
- }
- }
- }
- }
-
- /*
- * Now that we've checked whether we are connected, check whether we've
- * already timed out.
- *
- * First figure out how long time we have left to connect */
-
- allow = Curl_timeleft(data, &now, TRUE);
-
- if(allow < 0) {
- /* time-out, bail out, go home */
- failf(data, "Connection timeout after %ld ms",
- Curl_timediff(now, data->progress.t_startsingle));
- return CURLE_OPERATION_TIMEDOUT;
+ is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
+ closeit = (ctrl == CONNCTRL_CONNECTION) ||
+ ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
+ if((ctrl == CONNCTRL_STREAM) && is_multiplex)
+ ; /* stream signal on multiplex conn never affects close state */
+ else if((bit)closeit != conn->bits.close) {
+ conn->bits.close = closeit; /* the only place in the source code that
+ should assign this bit */
}
+}
- if(result &&
- (conn->tempsock[0] == CURL_SOCKET_BAD) &&
- (conn->tempsock[1] == CURL_SOCKET_BAD)) {
- /* no more addresses to try */
- const char *hostname;
- CURLcode failreason = result;
-
- /* if the first address family runs out of addresses to try before the
- happy eyeball timeout, go ahead and try the next family now */
- result = trynextip(data, conn, sockindex, 1);
- if(!result)
- return result;
+/**
+ * job walking the matching addr infos, creating a sub-cfilter with the
+ * provided method `cf_create` and running setup/connect on it.
+ */
+struct eyeballer {
+ const char *name;
+ const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
+ int ai_family; /* matching address family only */
+ cf_ip_connect_create *cf_create; /* for creating cf */
+ struct Curl_cfilter *cf; /* current sub-cfilter connecting */
+ struct eyeballer *primary; /* eyeballer this one is backup for */
+ timediff_t delay_ms; /* delay until start */
+ struct curltime started; /* start of current attempt */
+ timediff_t timeoutms; /* timeout for current attempt */
+ expire_id timeout_id; /* ID for Curl_expire() */
+ CURLcode result;
+ int error;
+ BIT(has_started); /* attempts have started */
+ BIT(is_done); /* out of addresses/time */
+ BIT(connected); /* cf has connected */
+};
- result = failreason;
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy)
- hostname = conn->socks_proxy.host.name;
- else if(conn->bits.httpproxy)
- hostname = conn->http_proxy.host.name;
- else
-#endif
- if(conn->bits.conn_to_host)
- hostname = conn->conn_to_host.name;
- else
- hostname = conn->host.name;
+typedef enum {
+ SCFST_INIT,
+ SCFST_WAITING,
+ SCFST_DONE
+} cf_connect_state;
- failf(data, "Failed to connect to %s port %u after "
- "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
- hostname, conn->port,
- Curl_timediff(now, data->progress.t_startsingle),
- curl_easy_strerror(result));
+struct cf_he_ctx {
+ int transport;
+ cf_ip_connect_create *cf_create;
+ const struct Curl_dns_entry *remotehost;
+ cf_connect_state state;
+ struct eyeballer *baller[2];
+ struct eyeballer *winner;
+ struct curltime started;
+};
- Curl_quic_disconnect(data, conn, 0);
- Curl_quic_disconnect(data, conn, 1);
+static CURLcode eyeballer_new(struct eyeballer **pballer,
+ cf_ip_connect_create *cf_create,
+ const struct Curl_addrinfo *addr,
+ int ai_family,
+ struct eyeballer *primary,
+ timediff_t delay_ms,
+ timediff_t timeout_ms,
+ expire_id timeout_id)
+{
+ struct eyeballer *baller;
-#ifdef WSAETIMEDOUT
- if(WSAETIMEDOUT == data->state.os_errno)
- result = CURLE_OPERATION_TIMEDOUT;
-#elif defined(ETIMEDOUT)
- if(ETIMEDOUT == data->state.os_errno)
- result = CURLE_OPERATION_TIMEDOUT;
-#endif
- }
- else
- result = CURLE_OK; /* still trying */
+ *pballer = NULL;
+ baller = calloc(1, sizeof(*baller) + 1000);
+ if(!baller)
+ return CURLE_OUT_OF_MEMORY;
- return result;
+ baller->name = ((ai_family == AF_INET)? "ipv4" : (
+#ifdef ENABLE_IPV6
+ (ai_family == AF_INET6)? "ipv6" :
+#endif
+ "ip"));
+ baller->cf_create = cf_create;
+ baller->addr = addr;
+ baller->ai_family = ai_family;
+ baller->primary = primary;
+ baller->delay_ms = delay_ms;
+ baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)?
+ timeout_ms / 2 : timeout_ms;
+ baller->timeout_id = timeout_id;
+ baller->result = CURLE_COULDNT_CONNECT;
+
+ *pballer = baller;
+ return CURLE_OK;
}
-static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
+static void baller_close(struct eyeballer *baller,
+ struct Curl_easy *data)
{
-#if defined(TCP_NODELAY)
- curl_socklen_t onoff = (curl_socklen_t) 1;
- int level = IPPROTO_TCP;
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char buffer[STRERROR_LEN];
-#else
- (void) data;
-#endif
-
- if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
- sizeof(onoff)) < 0)
- infof(data, "Could not set TCP_NODELAY: %s",
- Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-#else
- (void)data;
- (void)sockfd;
-#endif
+ if(baller && baller->cf) {
+ Curl_conn_cf_discard_chain(&baller->cf, data);
+ }
}
-#ifdef SO_NOSIGPIPE
-/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
- sending data to a dead peer (instead of relying on the 4th argument to send
- being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
- systems? */
-static void nosigpipe(struct Curl_easy *data,
- curl_socket_t sockfd)
+static void baller_free(struct eyeballer *baller,
+ struct Curl_easy *data)
{
- int onoff = 1;
- if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
- sizeof(onoff)) < 0) {
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char buffer[STRERROR_LEN];
- infof(data, "Could not set SO_NOSIGPIPE: %s",
- Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-#endif
+ if(baller) {
+ baller_close(baller, data);
+ free(baller);
}
}
-#else
-#define nosigpipe(x,y) Curl_nop_stmt
-#endif
-
-#ifdef USE_WINSOCK
-/* When you run a program that uses the Windows Sockets API, you may
- experience slow performance when you copy data to a TCP server.
-
- https://support.microsoft.com/kb/823764
- Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
- Buffer Size
-
- The problem described in this knowledge-base is applied only to pre-Vista
- Windows. Following function trying to detect OS version and skips
- SO_SNDBUF adjustment for Windows Vista and above.
-*/
-#define DETECT_OS_NONE 0
-#define DETECT_OS_PREVISTA 1
-#define DETECT_OS_VISTA_OR_LATER 2
-
-void Curl_sndbufset(curl_socket_t sockfd)
+static void baller_next_addr(struct eyeballer *baller)
{
- int val = CURL_MAX_WRITE_SIZE + 32;
- int curval = 0;
- int curlen = sizeof(curval);
-
- static int detectOsState = DETECT_OS_NONE;
-
- if(detectOsState == DETECT_OS_NONE) {
- if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL))
- detectOsState = DETECT_OS_VISTA_OR_LATER;
- else
- detectOsState = DETECT_OS_PREVISTA;
- }
-
- if(detectOsState == DETECT_OS_VISTA_OR_LATER)
- return;
-
- if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
- if(curval > val)
- return;
-
- setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+ baller->addr = addr_next_match(baller->addr, baller->ai_family);
}
-#endif
/*
- * singleipconnect()
+ * Initiate a connect attempt walk.
*
* Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
* CURL_SOCKET_BAD. Other errors will however return proper errors.
- *
- * singleipconnect() connects to the given IP only, and it may return without
- * having connected.
*/
-static CURLcode singleipconnect(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai,
- int tempindex)
+static void baller_initiate(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller)
{
- struct Curl_sockaddr_ex addr;
- int rc = -1;
- int error = 0;
- bool isconnected = FALSE;
- curl_socket_t sockfd;
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct Curl_cfilter *cf_prev = baller->cf;
+ struct Curl_cfilter *wcf;
CURLcode result;
- char ipaddress[MAX_IPADR_LEN];
- int port;
- bool is_tcp;
-#ifdef TCP_FASTOPEN_CONNECT
- int optval = 1;
-#endif
- const char *ipmsg;
- char buffer[STRERROR_LEN];
- curl_socket_t *sockp = &conn->tempsock[tempindex];
- *sockp = CURL_SOCKET_BAD;
- result = Curl_socket(data, ai, &addr, &sockfd);
+
+ /* Don't close a previous cfilter yet to ensure that the next IP's
+ socket gets a different file descriptor, which can prevent bugs when
+ the curl_multi_socket_action interface is used with certain select()
+ replacements such as kqueue. */
+ result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
+ ctx->transport);
if(result)
- return result;
+ goto out;
- /* store remote address and port used in this connection attempt */
- if(!Curl_addr2string(&addr.sa_addr, addr.addrlen,
- ipaddress, &port)) {
- /* malformed address or bug in inet_ntop, try next address */
- failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, sockfd);
- return CURLE_OK;
+ /* the new filter might have sub-filters */
+ for(wcf = baller->cf; wcf; wcf = wcf->next) {
+ wcf->conn = cf->conn;
+ wcf->sockindex = cf->sockindex;
}
-#ifdef ENABLE_IPV6
- if(addr.family == AF_INET6)
- ipmsg = " Trying [%s]:%d...";
- else
-#endif
- ipmsg = " Trying %s:%d...";
- infof(data, ipmsg, ipaddress, port);
-#ifdef ENABLE_IPV6
- is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
- addr.socktype == SOCK_STREAM;
-#else
- is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
-#endif
- if(is_tcp && data->set.tcp_nodelay)
- tcpnodelay(data, sockfd);
-
- nosigpipe(data, sockfd);
+ if(addr_next_match(baller->addr, baller->ai_family)) {
+ Curl_expire(data, baller->timeoutms, baller->timeout_id);
+ }
- Curl_sndbufset(sockfd);
+out:
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "%s failed", baller->name));
+ baller_close(baller, data);
+ }
+ if(cf_prev)
+ Curl_conn_cf_discard_chain(&cf_prev, data);
+ baller->result = result;
+}
- if(is_tcp && data->set.tcp_keepalive)
- tcpkeepalive(data, sockfd);
+/**
+ * Start a connection attempt on the current baller address.
+ * Will return CURLE_OK on the first address where a socket
+ * could be created and the non-blocking connect started.
+ * Returns error when all remaining addresses have been tried.
+ */
+static CURLcode baller_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ timediff_t timeoutms)
+{
+ baller->error = 0;
+ baller->connected = FALSE;
+ baller->has_started = TRUE;
+
+ while(baller->addr) {
+ baller->started = Curl_now();
+ baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
+ timeoutms / 2 : timeoutms;
+ baller_initiate(cf, data, baller);
+ if(!baller->result)
+ break;
+ baller_next_addr(baller);
+ }
+ if(!baller->addr) {
+ baller->is_done = TRUE;
+ }
+ return baller->result;
+}
- if(data->set.fsockopt) {
- /* activate callback for setting socket options */
- Curl_set_in_callback(data, true);
- error = data->set.fsockopt(data->set.sockopt_client,
- sockfd,
- CURLSOCKTYPE_IPCXN);
- Curl_set_in_callback(data, false);
- if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
- isconnected = TRUE;
- else if(error) {
- Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
- return CURLE_ABORTED_BY_CALLBACK;
- }
+/* Used within the multi interface. Try next IP address, returns error if no
+ more address exists or error */
+static CURLcode baller_start_next(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ timediff_t timeoutms)
+{
+ if(cf->sockindex == FIRSTSOCKET) {
+ baller_next_addr(baller);
+ baller_start(cf, data, baller, timeoutms);
+ }
+ else {
+ baller->error = 0;
+ baller->connected = FALSE;
+ baller->has_started = TRUE;
+ baller->is_done = TRUE;
+ baller->result = CURLE_COULDNT_CONNECT;
}
+ return baller->result;
+}
- /* possibly bind the local end to an IP, interface or port */
- if(addr.family == AF_INET
-#ifdef ENABLE_IPV6
- || addr.family == AF_INET6
+static CURLcode baller_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ struct curltime *now,
+ bool *connected)
+{
+ (void)cf;
+ *connected = baller->connected;
+ if(!baller->result && !*connected) {
+ /* evaluate again */
+ baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
+
+ if(!baller->result) {
+ if (*connected) {
+ baller->connected = TRUE;
+ baller->is_done = TRUE;
+ }
+ else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
+ infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T
+ "ms, move on!", baller->name, baller->timeoutms);
+#if defined(ETIMEDOUT)
+ baller->error = ETIMEDOUT;
#endif
- ) {
- result = bindlocal(data, conn, sockfd, addr.family,
- Curl_ipv6_scope(&addr.sa_addr));
- if(result) {
- Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
- if(result == CURLE_UNSUPPORTED_PROTOCOL) {
- /* The address family is not supported on this interface.
- We can continue trying addresses */
- return CURLE_COULDNT_CONNECT;
+ baller->result = CURLE_OPERATION_TIMEDOUT;
}
- return result;
}
}
+ return baller->result;
+}
- /* set socket non-blocking */
- (void)curlx_nonblock(sockfd, TRUE);
+/*
+ * is_connected() checks if the socket has connected.
+ */
+static CURLcode is_connected(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *connected)
+{
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct connectdata *conn = cf->conn;
+ CURLcode result;
+ struct curltime now;
+ size_t i;
+ int ongoing, not_started;
+ const char *hostname;
- conn->connecttime = Curl_now();
- if(conn->num_addr > 1) {
- Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
- Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
- }
+ /* Check if any of the conn->tempsock we use for establishing connections
+ * succeeded and, if so, close any ongoing other ones.
+ * Transfer the successful conn->tempsock to conn->sock[sockindex]
+ * and set conn->tempsock to CURL_SOCKET_BAD.
+ * If transport is QUIC, we need to shutdown the ongoing 'other'
+ * cot ballers in a QUIC appropriate way. */
+evaluate:
+ *connected = FALSE; /* a very negative world view is best */
+ now = Curl_now();
+ ongoing = not_started = 0;
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
- /* Connect TCP and QUIC sockets */
- if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
- if(conn->bits.tcp_fastopen) {
-#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
-# if defined(HAVE_BUILTIN_AVAILABLE)
- /* while connectx function is available since macOS 10.11 / iOS 9,
- it did not have the interface declared correctly until
- Xcode 9 / macOS SDK 10.13 */
- if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
- sa_endpoints_t endpoints;
- endpoints.sae_srcif = 0;
- endpoints.sae_srcaddr = NULL;
- endpoints.sae_srcaddrlen = 0;
- endpoints.sae_dstaddr = &addr.sa_addr;
- endpoints.sae_dstaddrlen = addr.addrlen;
-
- rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
- CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
- NULL, 0, NULL, NULL);
+ if(!baller || baller->is_done)
+ continue;
+
+ if(!baller->has_started) {
+ ++not_started;
+ continue;
+ }
+ baller->result = baller_connect(cf, data, baller, &now, connected);
+ DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d",
+ baller->name, baller->result, *connected));
+
+ if(!baller->result) {
+ if(*connected) {
+ /* connected, declare the winner */
+ ctx->winner = baller;
+ ctx->baller[i] = NULL;
+ break;
}
- else {
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ else { /* still waiting */
+ ++ongoing;
}
-# else
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
-# endif /* HAVE_BUILTIN_AVAILABLE */
-#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
- (void *)&optval, sizeof(optval)) < 0)
- infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
-
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
-#elif defined(MSG_FASTOPEN) /* old Linux */
- if(conn->given->flags & PROTOPT_SSL)
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
- else
- rc = 0; /* Do nothing */
-#endif
}
- else {
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ else if(!baller->is_done) {
+ /* The baller failed to connect, start its next attempt */
+ if(baller->error) {
+ data->state.os_errno = baller->error;
+ SET_SOCKERRNO(baller->error);
+ }
+ baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
+ if(baller->is_done) {
+ DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+ }
+ else {
+ /* next attempt was started */
+ DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name));
+ ++ongoing;
+ }
}
+ }
- if(-1 == rc)
- error = SOCKERRNO;
-#ifdef ENABLE_QUIC
- else if(conn->transport == TRNSPRT_QUIC) {
- /* pass in 'sockfd' separately since it hasn't been put into the
- tempsock array at this point */
- result = Curl_quic_connect(data, conn, sockfd, tempindex,
- &addr.sa_addr, addr.addrlen);
- if(result)
- error = SOCKERRNO;
+ if(ctx->winner) {
+ *connected = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Nothing connected, check the time before we might
+ * start new ballers or return ok. */
+ if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
+ failf(data, "Connection timeout after %ld ms",
+ Curl_timediff(now, data->progress.t_startsingle));
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* Check if we have any waiting ballers to start now. */
+ if(not_started > 0) {
+ int added = 0;
+
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+
+ if(!baller || baller->has_started)
+ continue;
+ /* We start its primary baller has failed to connect or if
+ * its start delay_ms have expired */
+ if((baller->primary && baller->primary->is_done) ||
+ Curl_timediff(now, ctx->started) >= baller->delay_ms) {
+ baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
+ if(baller->is_done) {
+ DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%ldms)",
+ baller->name, baller->timeoutms));
+ ++ongoing;
+ ++added;
+ }
+ }
}
-#endif
+ if(added > 0)
+ goto evaluate;
}
- else {
- *sockp = sockfd;
+
+ if(ongoing > 0) {
+ /* We are still trying, return for more waiting */
+ *connected = FALSE;
return CURLE_OK;
}
- if(-1 == rc) {
- switch(error) {
- case EINPROGRESS:
- case EWOULDBLOCK:
-#if defined(EAGAIN)
-#if (EAGAIN) != (EWOULDBLOCK)
- /* On some platforms EAGAIN and EWOULDBLOCK are the
- * same value, and on others they are different, hence
- * the odd #if
- */
- case EAGAIN:
-#endif
-#endif
- result = CURLE_OK;
+ /* all ballers have failed to connect. */
+ DEBUGF(LOG_CF(data, cf, "all eyeballers failed"));
+ result = CURLE_COULDNT_CONNECT;
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d",
+ baller?baller->name:NULL,
+ baller?baller->has_started:0,
+ baller?baller->result:0));
+ if(baller && baller->has_started && baller->result) {
+ result = baller->result;
break;
-
- default:
- /* unknown error, fallthrough and try another address! */
- infof(data, "Immediate connect fail for %s: %s",
- ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
- data->state.os_errno = error;
-
- /* connect failed */
- Curl_closesocket(data, conn, sockfd);
- result = CURLE_COULDNT_CONNECT;
}
}
- if(!result)
- *sockp = sockfd;
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy)
+ hostname = conn->socks_proxy.host.name;
+ else if(conn->bits.httpproxy)
+ hostname = conn->http_proxy.host.name;
+ else
+#endif
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+
+ failf(data, "Failed to connect to %s port %u after "
+ "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+ hostname, conn->port,
+ Curl_timediff(now, data->progress.t_startsingle),
+ curl_easy_strerror(result));
+
+#ifdef WSAETIMEDOUT
+ if(WSAETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#elif defined(ETIMEDOUT)
+ if(ETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#endif
return result;
}
/*
- * TCP connect to the given host with timeout, proxy or remote doesn't matter.
- * There might be more than one IP address to try out. Fill in the passed
- * pointer with the connected socket.
+ * Connect to the given host with timeout, proxy or remote doesn't matter.
+ * There might be more than one IP address to try out.
*/
-
-CURLcode Curl_connecthost(struct Curl_easy *data,
- struct connectdata *conn, /* context */
- const struct Curl_dns_entry *remotehost)
+static CURLcode start_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
{
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct connectdata *conn = cf->conn;
CURLcode result = CURLE_COULDNT_CONNECT;
- int i;
+ int ai_family0, ai_family1;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ const struct Curl_addrinfo *addr0, *addr1;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
@@ -1310,58 +745,76 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
- conn->num_addr = Curl_num_addresses(remotehost->addr);
- conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
- conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
-
- /* Max time for the next connection attempt */
- conn->timeoutms_per_addr[0] =
- conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
- conn->timeoutms_per_addr[1] =
- conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
+ ctx->started = Curl_now();
+ /* remotehost->addr is the list of addresses from the resolver, each
+ * with an address family. The list has at least one entry, possibly
+ * many more.
+ * We try at most 2 at a time, until we either get a connection or
+ * run out of addresses to try. Since likelihood of success is tied
+ * to the address family (e.g. IPV6 might not work at all ), we want
+ * the 2 connect attempt ballers to try different families, if possible.
+ *
+ */
if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
/* any IP version is allowed */
- conn->tempfamily[0] = conn->tempaddr[0]?
- conn->tempaddr[0]->ai_family:0;
+ ai_family0 = remotehost->addr?
+ remotehost->addr->ai_family : 0;
#ifdef ENABLE_IPV6
- conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
+ ai_family1 = ai_family0 == AF_INET6 ?
AF_INET : AF_INET6;
#else
- conn->tempfamily[1] = AF_UNSPEC;
+ ai_family1 = AF_UNSPEC;
#endif
}
else {
/* only one IP version is allowed */
- conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
+ ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
AF_INET :
#ifdef ENABLE_IPV6
AF_INET6;
#else
AF_UNSPEC;
#endif
- conn->tempfamily[1] = AF_UNSPEC;
-
- ainext(conn, 0, FALSE); /* find first address of the right type */
+ ai_family1 = AF_UNSPEC;
}
- ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
-
- DEBUGF(infof(data, "family0 == %s, family1 == %s",
- conn->tempfamily[0] == AF_INET ? "v4" : "v6",
- conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
+ /* Get the first address in the list that matches the family,
+ * this might give NULL, if we do not have any matches. */
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
+ addr1 = addr_first_match(remotehost->addr, ai_family1);
+ if(!addr0 && addr1) {
+ /* switch around, so a single baller always uses addr0 */
+ addr0 = addr1;
+ ai_family0 = ai_family1;
+ addr1 = NULL;
+ }
- /* get through the list in family order in case of quick failures */
- for(i = 0; (i < 2) && result; i++) {
- while(conn->tempaddr[i]) {
- result = singleipconnect(data, conn, conn->tempaddr[i], i);
- if(!result)
- break;
- ainext(conn, i, TRUE);
- }
+ /* We found no address that matches our criteria, we cannot connect */
+ if(!addr0) {
+ return CURLE_COULDNT_CONNECT;
}
+
+ memset(ctx->baller, 0, sizeof(ctx->baller));
+ result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
+ NULL, 0, /* no primary/delay, start now */
+ timeout_ms, EXPIRE_DNS_PER_NAME);
if(result)
return result;
+ DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)",
+ ctx->baller[0]->name, ctx->baller[0]->timeoutms));
+ if(addr1) {
+ /* second one gets a delayed start */
+ result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
+ ctx->baller[0], /* wait on that to fail */
+ /* or start this delayed */
+ data->set.happy_eyeballs_timeout,
+ timeout_ms, EXPIRE_DNS_PER_NAME2);
+ if(result)
+ return result;
+ DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)",
+ ctx->baller[1]->name, ctx->baller[1]->timeoutms));
+ }
Curl_expire(data, data->set.happy_eyeballs_timeout,
EXPIRE_HAPPY_EYEBALLS);
@@ -1369,310 +822,335 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
return CURLE_OK;
}
-struct connfind {
- long id_tofind;
- struct connectdata *found;
-};
-
-static int conn_is_conn(struct Curl_easy *data,
- struct connectdata *conn, void *param)
+static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct connfind *f = (struct connfind *)param;
- (void)data;
- if(conn->connection_id == f->id_tofind) {
- f->found = conn;
- return 1;
+ struct cf_he_ctx *ctx = cf->ctx;
+ size_t i;
+
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(data);
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ baller_free(ctx->baller[i], data);
+ ctx->baller[i] = NULL;
}
- return 0;
+ baller_free(ctx->winner, data);
+ ctx->winner = NULL;
}
-/*
- * Used to extract socket and connectdata struct for the most recent
- * transfer on the given Curl_easy.
- *
- * The returned socket will be CURL_SOCKET_BAD in case of failure!
- */
-curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
- struct connectdata **connp)
+static int cf_he_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
{
- DEBUGASSERT(data);
+ struct cf_he_ctx *ctx = cf->ctx;
+ size_t i, s;
+ int wrc, rc = GETSOCK_BLANK;
+ curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE];
- /* this works for an easy handle:
- * - that has been used for curl_easy_perform()
- * - that is associated with a multi handle, and whose connection
- * was detached with CURLOPT_CONNECT_ONLY
- */
- if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
- struct connectdata *c;
- struct connfind find;
- find.id_tofind = data->state.lastconnect_id;
- find.found = NULL;
+ if(cf->connected)
+ return cf->next->cft->get_select_socks(cf->next, data, socks);
- Curl_conncache_foreach(data,
- data->share && (data->share->specifier
- & (1<< CURL_LOCK_DATA_CONNECT))?
- &data->share->conn_cache:
- data->multi_easy?
- &data->multi_easy->conn_cache:
- &data->multi->conn_cache, &find, conn_is_conn);
+ for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ if(!baller || !baller->cf)
+ continue;
- if(!find.found) {
- data->state.lastconnect_id = -1;
- return CURL_SOCKET_BAD;
+ wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks);
+ if(wrc) {
+ /* TODO: we assume we get at most one socket back */
+ socks[s] = wsocks[0];
+ if(wrc & GETSOCK_WRITESOCK(0))
+ rc |= GETSOCK_WRITESOCK(s);
+ if(wrc & GETSOCK_READSOCK(0))
+ rc |= GETSOCK_READSOCK(s);
+ s++;
}
-
- c = find.found;
- if(connp)
- /* only store this if the caller cares for it */
- *connp = c;
- return c->sock[FIRSTSOCKET];
}
- return CURL_SOCKET_BAD;
+ return rc;
}
-/*
- * Check if a connection seems to be alive.
- */
-bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode cf_he_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- (void)data;
- /* First determine if ssl */
- if(Curl_conn_is_ssl(data, FIRSTSOCKET)) {
- /* use the SSL context */
- if(!Curl_ssl_check_cxn(data, conn))
- return false; /* FIN received */
+ struct cf_he_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
}
-/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
-#ifdef MSG_PEEK
- else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
- return false;
- else {
- /* use the socket */
- char buf;
- if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
- (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
- return false; /* FIN received */
- }
+
+ (void)blocking; /* TODO: do we want to support this? */
+ DEBUGASSERT(ctx);
+ *done = FALSE;
+
+ switch(ctx->state) {
+ case SCFST_INIT:
+ DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
+ DEBUGASSERT(!cf->connected);
+ result = start_connect(cf, data, ctx->remotehost);
+ if(result)
+ return result;
+ ctx->state = SCFST_WAITING;
+ /* FALLTHROUGH */
+ case SCFST_WAITING:
+ result = is_connected(cf, data, done);
+ if(!result && *done) {
+ DEBUGASSERT(ctx->winner);
+ DEBUGASSERT(ctx->winner->cf);
+ DEBUGASSERT(ctx->winner->cf->connected);
+ /* we have a winner. Install and activate it.
+ * close/free all others. */
+ ctx->state = SCFST_DONE;
+ cf->connected = TRUE;
+ cf->next = ctx->winner->cf;
+ ctx->winner->cf = NULL;
+ cf_he_ctx_clear(cf, data);
+ Curl_conn_cf_cntrl(cf->next, data, TRUE,
+ CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+
+ if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+ Curl_verboseconnect(data, cf->conn);
+ data->info.numconnects++; /* to track the # of connections made */
+ }
+ break;
+ case SCFST_DONE:
+ *done = TRUE;
+ break;
}
-#endif
- return true;
+ return result;
}
-/*
- * Close a socket.
- *
- * 'conn' can be NULL, beware!
- */
-int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sock)
+static void cf_he_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- if(conn && conn->fclosesocket) {
- if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
- /* if this socket matches the second socket, and that was created with
- accept, then we MUST NOT call the callback but clear the accepted
- status */
- conn->bits.sock_accepted = FALSE;
- else {
- int rc;
- Curl_multi_closed(data, sock);
- Curl_set_in_callback(data, true);
- rc = conn->fclosesocket(conn->closesocket_client, sock);
- Curl_set_in_callback(data, false);
- return rc;
- }
- }
+ struct cf_he_ctx *ctx = cf->ctx;
- if(conn)
- /* tell the multi-socket code about this */
- Curl_multi_closed(data, sock);
-
- sclose(sock);
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf_he_ctx_clear(cf, data);
+ cf->connected = FALSE;
+ ctx->state = SCFST_INIT;
- return 0;
+ if(cf->next) {
+ cf->next->cft->close(cf->next, data);
+ Curl_conn_cf_discard_chain(&cf->next, data);
+ }
}
-/*
- * Create a socket based on info from 'conn' and 'ai'.
- *
- * 'addr' should be a pointer to the correct struct to get data back, or NULL.
- * 'sockfd' must be a pointer to a socket descriptor.
- *
- * If the open socket callback is set, used that!
- *
- */
-CURLcode Curl_socket(struct Curl_easy *data,
- const struct Curl_addrinfo *ai,
- struct Curl_sockaddr_ex *addr,
- curl_socket_t *sockfd)
+static bool cf_he_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- struct Curl_sockaddr_ex dummy;
-
- if(!addr)
- /* if the caller doesn't want info back, use a local temp copy */
- addr = &dummy;
-
- /*
- * The Curl_sockaddr_ex structure is basically 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 cf_he_ctx *ctx = cf->ctx;
+ size_t i;
- addr->family = ai->ai_family;
- switch(conn->transport) {
- case TRNSPRT_TCP:
- addr->socktype = SOCK_STREAM;
- addr->protocol = IPPROTO_TCP;
- break;
- case TRNSPRT_UNIX:
- addr->socktype = SOCK_STREAM;
- addr->protocol = IPPROTO_IP;
- break;
- default: /* UDP and QUIC */
- addr->socktype = SOCK_DGRAM;
- addr->protocol = IPPROTO_UDP;
- break;
- }
- addr->addrlen = ai->ai_addrlen;
-
- if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
- addr->addrlen = sizeof(struct Curl_sockaddr_storage);
- memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
-
- if(data->set.fopensocket) {
- /*
- * If the opensocket callback is set, all the destination address
- * information is passed to the callback. Depending on this information the
- * callback may opt to abort the connection, this is indicated returning
- * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
- * the callback returns a valid socket the destination address information
- * might have been changed and this 'new' address will actually be used
- * here to connect.
- */
- Curl_set_in_callback(data, true);
- *sockfd = data->set.fopensocket(data->set.opensocket_client,
- CURLSOCKTYPE_IPCXN,
- (struct curl_sockaddr *)addr);
- Curl_set_in_callback(data, false);
- }
- else
- /* opensocket callback not set, so simply create the socket now */
- *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+ if(cf->connected)
+ return cf->next->cft->has_data_pending(cf->next, data);
- if(*sockfd == CURL_SOCKET_BAD)
- /* no socket, no connection */
- return CURLE_COULDNT_CONNECT;
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ if(!baller || !baller->cf)
+ continue;
+ if(baller->cf->cft->has_data_pending(baller->cf, data))
+ return TRUE;
+ }
+ return FALSE;
+}
- if(conn->transport == TRNSPRT_QUIC) {
- /* QUIC sockets need to be nonblocking */
- (void)curlx_nonblock(*sockfd, TRUE);
- switch(addr->family) {
-#if defined(__linux__) && defined(IP_MTU_DISCOVER)
- case AF_INET: {
- int val = IP_PMTUDISC_DO;
- (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
- sizeof(val));
- break;
+static CURLcode cf_he_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_he_ctx *ctx = cf->ctx;
+
+ if(!cf->connected) {
+ switch(query) {
+ case CF_QUERY_CONNECT_REPLY_MS: {
+ int reply_ms = -1;
+ size_t i;
+
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ int breply_ms;
+
+ if(baller && baller->cf &&
+ !baller->cf->cft->query(baller->cf, data, query,
+ &breply_ms, NULL)) {
+ if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
+ reply_ms = breply_ms;
+ }
+ }
+ *pres1 = reply_ms;
+ DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1));
+ return CURLE_OK;
}
-#endif
-#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
- case AF_INET6: {
- int val = IPV6_PMTUDISC_DO;
- (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
- sizeof(val));
+
+ default:
break;
}
-#endif
- }
}
-#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
- if(conn->scope_id && (addr->family == AF_INET6)) {
- struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
- sa6->sin6_scope_id = conn->scope_id;
- }
-#endif
-
- return CURLE_OK;
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
}
-/*
- * Curl_conncontrol() marks streams or connection for closure.
- */
-void Curl_conncontrol(struct connectdata *conn,
- int ctrl /* see defines in header */
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- , const char *reason
-#endif
- )
+static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- /* close if a connection, or a stream that isn't multiplexed. */
- /* This function will be called both before and after this connection is
- associated with a transfer. */
- bool closeit;
- DEBUGASSERT(conn);
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- (void)reason; /* useful for debugging */
-#endif
- closeit = (ctrl == CONNCTRL_CONNECTION) ||
- ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
- if((ctrl == CONNCTRL_STREAM) &&
- (conn->handler->flags & PROTOPT_STREAM))
- ;
- else if((bit)closeit != conn->bits.close) {
- conn->bits.close = closeit; /* the only place in the source code that
- should assign this bit */
+ struct cf_he_ctx *ctx = cf->ctx;
+
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ if(ctx) {
+ cf_he_ctx_clear(cf, data);
}
+ /* release any resources held in state */
+ Curl_safefree(ctx);
}
-typedef enum {
- SCFST_INIT,
- SCFST_WAITING,
- SCFST_DONE
-} cf_connect_state;
-
-struct socket_cf_ctx {
- const struct Curl_dns_entry *remotehost;
- cf_connect_state state;
+struct Curl_cftype Curl_cft_happy_eyeballs = {
+ "HAPPY-EYEBALLS",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_he_destroy,
+ cf_he_connect,
+ cf_he_close,
+ Curl_cf_def_get_host,
+ cf_he_get_select_socks,
+ cf_he_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,
+ cf_he_query,
};
-static int socket_cf_get_select_socks(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- curl_socket_t *socks)
+CURLcode Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ cf_ip_connect_create *cf_create,
+ const struct Curl_dns_entry *remotehost,
+ int transport)
{
- struct connectdata *conn = cf->conn;
- int i, s, rc = GETSOCK_BLANK;
+ struct cf_he_ctx *ctx = NULL;
+ CURLcode result;
(void)data;
- if(cf->connected) {
- return rc;
+ (void)conn;
+ *pcf = NULL;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->transport = transport;
+ ctx->cf_create = cf_create;
+ ctx->remotehost = remotehost;
+
+ result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
+
+out:
+ if(result) {
+ Curl_safefree(*pcf);
+ Curl_safefree(ctx);
}
+ return result;
+}
+
+struct transport_provider {
+ int transport;
+ cf_ip_connect_create *cf_create;
+};
- for(i = s = 0; i<2; i++) {
- if(conn->tempsock[i] != CURL_SOCKET_BAD) {
- socks[s] = conn->tempsock[i];
- rc |= GETSOCK_WRITESOCK(s);
+static
+#ifndef DEBUGBUILD
+const
+#endif
+struct transport_provider transport_providers[] = {
+ { TRNSPRT_TCP, Curl_cf_tcp_create },
#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC)
- /* when connecting QUIC, we want to read the socket too */
- rc |= GETSOCK_READSOCK(s);
+ { TRNSPRT_QUIC, Curl_cf_quic_create },
#endif
- s++;
+ { TRNSPRT_UDP, Curl_cf_udp_create },
+ { TRNSPRT_UNIX, Curl_cf_unix_create },
+};
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+static cf_ip_connect_create *get_cf_create(int transport)
+{
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ if(transport == transport_providers[i].transport)
+ return transport_providers[i].cf_create;
+ }
+ return NULL;
+}
+
+#ifdef DEBUGBUILD
+void Curl_debug_set_transport_provider(int transport,
+ cf_ip_connect_create *cf_create)
+{
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ if(transport == transport_providers[i].transport) {
+ transport_providers[i].cf_create = cf_create;
+ return;
}
}
+}
+#endif /* DEBUGBUILD */
- return rc;
+static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int 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) {
+ DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport));
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ result = Curl_cf_happy_eyeballs_create(&cf, data, cf_at->conn,
+ cf_create, remotehost,
+ transport);
+ if(result)
+ return result;
+
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return CURLE_OK;
}
-static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking, bool *done)
+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;
+ const struct Curl_dns_entry *remotehost;
+ int ssl_mode;
+ int transport;
+};
+
+static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- struct connectdata *conn = cf->conn;
- int sockindex = cf->sockindex;
- struct socket_cf_ctx *ctx = cf->ctx;
+ struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(cf->connected) {
@@ -1680,253 +1158,241 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- (void)blocking;
- DEBUGASSERT(ctx);
- *done = FALSE;
- switch(ctx->state) {
- case SCFST_INIT:
- DEBUGASSERT(CURL_SOCKET_BAD == conn->sock[sockindex]);
- DEBUGASSERT(!cf->connected);
- result = Curl_connecthost(data, conn, ctx->remotehost);
- if(!result)
- ctx->state = SCFST_WAITING;
- break;
- case SCFST_WAITING:
- result = is_connected(data, conn, sockindex, done);
- if(!result && *done) {
- Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
- if(Curl_conn_is_ssl(data, FIRSTSOCKET) ||
- (conn->handler->protocol & PROTO_FAMILY_SSH))
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
- post_connect(data, conn, sockindex);
- ctx->state = SCFST_DONE;
- cf->connected = TRUE;
- }
- break;
- case SCFST_DONE:
- *done = TRUE;
- break;
+ /* connect current sub-chain */
+connect_sub_chain:
+ if(cf->next && !cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
}
- return result;
-}
-static CURLcode socket_cf_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
-{
- struct socket_cf_ctx *ctx = cf->ctx;
+ if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
+ result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
+ if(result)
+ return result;
+ ctx->state = CF_SETUP_CNNCT_EYEBALLS;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
+ }
- (void)data;
- DEBUGASSERT(ctx);
- if(ctx->remotehost != remotehost) {
- if(ctx->remotehost) {
- /* switching dns entry? TODO: reset? */
- }
- ctx->remotehost = remotehost;
+ /* sub-chain connected, do we need to add more? */
+#ifndef CURL_DISABLE_PROXY
+ if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
+ result = Curl_cf_socks_proxy_insert_after(cf, data);
+ if(result)
+ return result;
+ ctx->state = CF_SETUP_CNNCT_SOCKS;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
}
- DEBUGF(infof(data, CFMSG(cf, "setup(remotehost=%s)"),
- cf->conn->hostname_resolve));
- return CURLE_OK;
-}
-static void socket_cf_close(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- int sockindex = cf->sockindex;
- struct socket_cf_ctx *ctx = cf->ctx;
-
- DEBUGASSERT(ctx);
- /* close possibly still open sockets */
- if(CURL_SOCKET_BAD != cf->conn->sock[sockindex]) {
- Curl_closesocket(data, cf->conn, cf->conn->sock[sockindex]);
- cf->conn->sock[sockindex] = CURL_SOCKET_BAD;
+ if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
+#ifdef USE_SSL
+ if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS
+ && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
+ result = Curl_cf_ssl_proxy_insert_after(cf, data);
+ if(result)
+ return result;
+ }
+#endif /* USE_SSL */
+
+#if !defined(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;
}
- if(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) {
- Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]);
- cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD;
+#endif /* !CURL_DISABLE_PROXY */
+
+ if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
+#if !defined(CURL_DISABLE_PROXY)
+ if(data->set.haproxyprotocol) {
+ if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
+ failf(data, "haproxy protocol not support 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;
}
- cf->connected = FALSE;
- ctx->state = SCFST_INIT;
-}
-
-static void socket_cf_get_host(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char **phost,
- const char **pdisplay_host,
- int *pport)
-{
- (void)data;
- *phost = cf->conn->host.name;
- *pdisplay_host = cf->conn->host.dispname;
- *pport = cf->conn->port;
-}
-static bool socket_cf_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
-{
- int readable;
- (void)data;
- DEBUGASSERT(cf);
+ if(ctx->state < CF_SETUP_CNNCT_SSL) {
+#ifdef USE_SSL
+ if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
+ || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
+ && cf->conn->handler->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;
+ }
- readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
- return (readable > 0 && (readable & CURL_CSELECT_IN));
+ ctx->state = CF_SETUP_DONE;
+ cf->connected = TRUE;
+ *done = TRUE;
+ return CURLE_OK;
}
-static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+static void cf_setup_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- ssize_t nwritten;
- nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
- return nwritten;
-}
+ struct cf_setup_ctx *ctx = cf->ctx;
-static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
- char *buf, size_t len, CURLcode *err)
-{
- ssize_t nread;
- nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
- return nread;
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf->connected = FALSE;
+ ctx->state = CF_SETUP_INIT;
+
+ if(cf->next) {
+ cf->next->cft->close(cf->next, data);
+ Curl_conn_cf_discard_chain(&cf->next, data);
+ }
}
-static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct socket_cf_ctx *state = cf->ctx;
+ struct cf_setup_ctx *ctx = cf->ctx;
(void)data;
- if(cf->connected) {
- socket_cf_close(cf, data);
- }
- /* release any resources held in state */
- Curl_safefree(state);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ Curl_safefree(ctx);
}
-static const struct Curl_cftype cft_socket = {
- "SOCKET",
- CF_TYPE_IP_CONNECT,
- socket_cf_destroy,
- socket_cf_setup,
- socket_cf_connect,
- socket_cf_close,
- socket_cf_get_host,
- socket_cf_get_select_socks,
- socket_cf_data_pending,
- socket_cf_send,
- socket_cf_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
+
+struct Curl_cftype Curl_cft_setup = {
+ "SETUP",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_setup_destroy,
+ cf_setup_connect,
+ cf_setup_close,
+ Curl_cf_def_get_host,
+ Curl_cf_def_get_select_socks,
+ 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,
};
-CURLcode Curl_conn_socket_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- CURLcode result;
struct Curl_cfilter *cf = NULL;
- struct socket_cf_ctx *scf_ctx = NULL;
+ struct cf_setup_ctx *ctx;
+ CURLcode result = CURLE_OK;
- /* Need to be first */
- DEBUGASSERT(conn);
- DEBUGASSERT(!conn->cfilter[sockindex]);
- scf_ctx = calloc(sizeof(*scf_ctx), 1);
- if(!scf_ctx) {
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- result = Curl_cf_create(&cf, &cft_socket, scf_ctx);
+ ctx->state = CF_SETUP_INIT;
+ ctx->remotehost = remotehost;
+ ctx->ssl_mode = ssl_mode;
+ ctx->transport = transport;
+
+ result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
if(result)
goto out;
- Curl_conn_cf_add(data, conn, sockindex, cf);
+ ctx = NULL;
out:
- if(result) {
- Curl_safefree(cf);
- Curl_safefree(scf_ctx);
- }
+ *pcf = result? NULL : cf;
+ free(ctx);
return result;
}
-static CURLcode socket_accept_cf_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking, bool *done)
+CURLcode Curl_cf_setup_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- /* we start accepted, if we ever close, we cannot go on */
- (void)data;
- (void)blocking;
- if(cf->connected) {
- *done = TRUE;
- return CURLE_OK;
- }
- return CURLE_FAILED_INIT;
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+out:
+ return result;
}
-static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
+CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- /* we start accepted, if we ever close, we cannot go on */
- (void)data;
- (void)remotehost;
- if(cf->connected) {
- return CURLE_OK;
- }
- return CURLE_FAILED_INIT;
-}
+ struct Curl_cfilter *cf;
+ CURLcode result;
-static const struct Curl_cftype cft_socket_accept = {
- "SOCKET-ACCEPT",
- CF_TYPE_IP_CONNECT,
- socket_cf_destroy,
- socket_accept_cf_setup,
- socket_accept_cf_connect,
- socket_cf_close,
- socket_cf_get_host, /* TODO: not accurate */
- Curl_cf_def_get_select_socks,
- socket_cf_data_pending,
- socket_cf_send,
- socket_cf_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
-};
+ DEBUGASSERT(data);
+ result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+out:
+ return result;
+}
-CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex, curl_socket_t *s)
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode)
{
- CURLcode result;
- struct Curl_cfilter *cf = NULL;
- struct socket_cf_ctx *scf_ctx = NULL;
+ CURLcode result = CURLE_OK;
- cf = conn->cfilter[sockindex];
- if(cf && cf->cft == &cft_socket_accept) {
- /* already an accept filter installed, just replace the socket */
- scf_ctx = cf->ctx;
- result = CURLE_OK;
- }
- else {
- /* replace any existing */
- Curl_conn_cf_discard_all(data, conn, sockindex);
- scf_ctx = calloc(sizeof(*scf_ctx), 1);
- if(!scf_ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- result = Curl_cf_create(&cf, &cft_socket_accept, scf_ctx);
+ DEBUGASSERT(data);
+ DEBUGASSERT(conn->handler);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+ if(!conn->cfilter[sockindex] &&
+ conn->handler->protocol == CURLPROTO_HTTPS &&
+ (ssl_mode == CURL_CF_SSL_ENABLE || ssl_mode != CURL_CF_SSL_DISABLE)) {
+
+ result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
if(result)
goto out;
- Curl_conn_cf_add(data, conn, sockindex, cf);
}
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
- /* close any existing socket and replace */
- Curl_closesocket(data, conn, conn->sock[sockindex]);
- conn->sock[sockindex] = *s;
- conn->bits.sock_accepted = TRUE;
- cf->connected = TRUE;
- scf_ctx->state = SCFST_DONE;
+ /* Still no cfilter set, apply default. */
+ if(!conn->cfilter[sockindex]) {
+ result = Curl_cf_setup_add(data, conn, sockindex, remotehost,
+ conn->transport, ssl_mode);
+ if(result)
+ goto out;
+ }
+ DEBUGASSERT(conn->cfilter[sockindex]);
out:
- if(result) {
- Curl_safefree(cf);
- Curl_safefree(scf_ctx);
- }
return result;
}
+
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h
index 1e90a8561e..e4fa10c4cb 100644
--- a/Utilities/cmcurl/lib/connect.h
+++ b/Utilities/cmcurl/lib/connect.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,9 +29,7 @@
#include "sockaddr.h"
#include "timeval.h"
-CURLcode Curl_connecthost(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_dns_entry *host);
+struct Curl_dns_entry;
/* generic function that returns how much time there's left to run, according
to the timeouts set */
@@ -53,67 +51,8 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
-/*
- * Check if a connection seems to be alive.
- */
-bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn);
-
-#ifdef USE_WINSOCK
-/* When you run a program that uses the Windows Sockets API, you may
- experience slow performance when you copy data to a TCP server.
-
- https://support.microsoft.com/kb/823764
-
- Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
- Buffer Size
-
-*/
-void Curl_sndbufset(curl_socket_t sockfd);
-#else
-#define Curl_sndbufset(y) Curl_nop_stmt
-#endif
-
-void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd);
-void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd);
-void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
- char *local_ip, int *local_port);
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
char *local_ip, int local_port);
-int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sock);
-
-/*
- * The Curl_sockaddr_ex structure is basically 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 addr;
- struct Curl_sockaddr_storage buff;
- } _sa_ex_u;
-};
-#define sa_addr _sa_ex_u.addr
-
-/*
- * Create a socket based on info from 'conn' and 'ai'.
- *
- * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
- * socket callback is set, used that!
- *
- */
-CURLcode Curl_socket(struct Curl_easy *data,
- const struct Curl_addrinfo *ai,
- struct Curl_sockaddr_ex *addr,
- curl_socket_t *sockfd);
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
@@ -148,13 +87,71 @@ void Curl_conncontrol(struct connectdata *conn,
#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP)
#endif
-CURLcode Curl_conn_socket_set(struct Curl_easy *data,
+/**
+ * Create a cfilter for making an "ip" connection to the
+ * given address, using parameters from `conn`. The "ip" connection
+ * can be a TCP socket, a UDP socket or even a QUIC connection.
+ *
+ * It MUST use only the supplied `ai` for its connection attempt.
+ *
+ * Such a filter may be used in "happy eyeball" scenarios, and its
+ * `connect` implementation needs to support non-blocking. 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 connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Create a 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.
+ */
+CURLcode
+Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
struct connectdata *conn,
- int sockindex);
-
-CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- curl_socket_t *s);
+ cf_ip_connect_create *cf_create,
+ const struct Curl_dns_entry *remotehost,
+ int transport);
+
+CURLcode Curl_cf_setup_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode);
+
+CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int 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.
+ */
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode);
+
+extern struct Curl_cftype Curl_cft_happy_eyeballs;
+extern struct Curl_cftype Curl_cft_setup;
+
+#ifdef DEBUGBUILD
+void Curl_debug_set_transport_provider(int transport,
+ cf_ip_connect_create *cf_create);
+#endif
#endif /* HEADER_CURL_CONNECT_H */
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index 9e345b197e..a29d2041b6 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -977,7 +977,8 @@ static const struct content_encoding error_encoding = {
static struct contenc_writer *
new_unencoding_writer(struct Curl_easy *data,
const struct content_encoding *handler,
- struct contenc_writer *downstream)
+ struct contenc_writer *downstream,
+ int order)
{
struct contenc_writer *writer;
@@ -987,6 +988,7 @@ new_unencoding_writer(struct Curl_easy *data,
if(writer) {
writer->handler = handler;
writer->downstream = downstream;
+ writer->order = order;
if(handler->init_writer(data, writer)) {
free(writer);
writer = NULL;
@@ -1042,10 +1044,10 @@ static const struct content_encoding *find_encoding(const char *name,
/* Set-up the unencoding stack from the Content-Encoding header value.
* See RFC 7231 section 3.1.2.2. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked)
+ const char *enclist, int is_transfer)
{
struct SingleRequest *k = &data->req;
- int counter = 0;
+ unsigned int order = is_transfer? 2: 1;
do {
const char *name;
@@ -1062,7 +1064,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
namelen = enclist - name + 1;
/* Special case: chunked encoding is handled at the reader level. */
- if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
+ if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) {
k->chunk = TRUE; /* chunks coming our way. */
Curl_httpchunk_init(data); /* init our chunky engine. */
}
@@ -1071,7 +1073,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
struct contenc_writer *writer;
if(!k->writer_stack) {
- k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL);
+ k->writer_stack = new_unencoding_writer(data, &client_encoding,
+ NULL, 0);
if(!k->writer_stack)
return CURLE_OUT_OF_MEMORY;
@@ -1080,16 +1083,29 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
if(!encoding)
encoding = &error_encoding; /* Defer error at stack use. */
- if(++counter >= MAX_ENCODE_STACK) {
- failf(data, "Reject response due to %u content encodings",
- counter);
+ if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) {
+ failf(data, "Reject response due to more than %u content encodings",
+ MAX_ENCODE_STACK);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Stack the unencoding stage. */
- writer = new_unencoding_writer(data, encoding, k->writer_stack);
- if(!writer)
- return CURLE_OUT_OF_MEMORY;
- k->writer_stack = writer;
+ if(order >= k->writer_stack->order) {
+ writer = new_unencoding_writer(data, encoding,
+ k->writer_stack, order);
+ if(!writer)
+ return CURLE_OUT_OF_MEMORY;
+ k->writer_stack = writer;
+ }
+ else {
+ struct contenc_writer *w = k->writer_stack;
+ while(w->downstream && order < w->downstream->order)
+ w = w->downstream;
+ writer = new_unencoding_writer(data, encoding,
+ w->downstream, order);
+ if(!writer)
+ return CURLE_OUT_OF_MEMORY;
+ w->downstream = writer;
+ }
}
} while(*enclist);
@@ -1099,11 +1115,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
#else
/* Stubs for builds without HTTP. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked)
+ const char *enclist, int is_transfer)
{
(void) data;
(void) enclist;
- (void) maybechunked;
+ (void) is_transfer;
return CURLE_NOT_BUILT_IN;
}
diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h
index 3c278cf727..56e7f97f70 100644
--- a/Utilities/cmcurl/lib/content_encoding.h
+++ b/Utilities/cmcurl/lib/content_encoding.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
struct contenc_writer {
const struct content_encoding *handler; /* Encoding handler. */
struct contenc_writer *downstream; /* Downstream writer. */
+ unsigned int order; /* Ordering within writer stack. */
};
/* Content encoding writer. */
@@ -46,7 +47,7 @@ struct content_encoding {
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked);
+ const char *enclist, int is_transfer);
CURLcode Curl_unencode_write(struct Curl_easy *data,
struct contenc_writer *writer,
const char *buf, size_t nbytes);
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index bccf2e8882..c457b2d95f 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -329,7 +329,7 @@ static char *sanitize_cookie_path(const char *cookie_path)
*/
void Curl_cookie_loadfiles(struct Curl_easy *data)
{
- struct curl_slist *list = data->state.cookielist;
+ struct curl_slist *list = data->set.cookielist;
if(list) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
while(list) {
@@ -347,8 +347,6 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
data->cookies = newcookies;
list = list->next;
}
- curl_slist_free_all(data->state.cookielist); /* clean up list */
- data->state.cookielist = NULL; /* don't do this again! */
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
}
@@ -1301,7 +1299,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
*/
remove_expired(c);
- if(fromfile && fp)
+ if(fromfile)
fclose(fp);
}
@@ -1800,12 +1798,10 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
CURLcode res;
if(data->set.str[STRING_COOKIEJAR]) {
- if(data->state.cookielist) {
- /* If there is a list of cookie files to read, do it first so that
- we have all the told files read before we write the new jar.
- Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
- Curl_cookie_loadfiles(data);
- }
+ /* If there is a list of cookie files to read, do it first so that
+ we have all the told files read before we write the new jar.
+ Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
+ Curl_cookie_loadfiles(data);
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@@ -1816,12 +1812,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
}
else {
- if(cleanup && data->state.cookielist) {
- /* since nothing is written, we can just free the list of cookie file
- names */
- curl_slist_free_all(data->state.cookielist); /* clean up list */
- data->state.cookielist = NULL;
- }
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
}
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index abc0a2e8a0..39bb08bc49 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c
index bcea883564..35a06350d8 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.c
+++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h
index b778121a7c..c757c49c5c 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.h
+++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h
index 85368a163c..806d4431cf 100644
--- a/Utilities/cmcurl/lib/curl_base64.h
+++ b/Utilities/cmcurl/lib/curl_base64.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index 9cde948f1c..6e2458dc8b 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h
index dc6b8cab86..1d1d60c28d 100644
--- a/Utilities/cmcurl/lib/curl_ctype.h
+++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,7 @@
#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f'))
#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F'))
-#define ISLOWCNTRL(x) ((x) >= 0 && ((x) <= 0x1f))
+#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f)
#define IS7F(x) ((x) == 0x7f)
#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d))
diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c
index a2bf648c29..5c623b35bc 100644
--- a/Utilities/cmcurl/lib/curl_des.c
+++ b/Utilities/cmcurl/lib/curl_des.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h
index c1c167471a..6ec450accc 100644
--- a/Utilities/cmcurl/lib/curl_des.h
+++ b/Utilities/cmcurl/lib/curl_des.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c
index 3cc7734f31..11c662a4c7 100644
--- a/Utilities/cmcurl/lib/curl_endian.c
+++ b/Utilities/cmcurl/lib/curl_endian.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_endian.h b/Utilities/cmcurl/lib/curl_endian.h
index 08d52e7c52..fa283214b1 100644
--- a/Utilities/cmcurl/lib/curl_endian.h
+++ b/Utilities/cmcurl/lib/curl_endian.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c
index b8a85a96ec..5f9ca4f1be 100644
--- a/Utilities/cmcurl/lib/curl_fnmatch.c
+++ b/Utilities/cmcurl/lib/curl_fnmatch.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.h b/Utilities/cmcurl/lib/curl_fnmatch.h
index 8324be533d..595646ff0d 100644
--- a/Utilities/cmcurl/lib/curl_fnmatch.h
+++ b/Utilities/cmcurl/lib/curl_fnmatch.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c
index 0d8c285a1d..686abe7511 100644
--- a/Utilities/cmcurl/lib/curl_get_line.c
+++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_get_line.h b/Utilities/cmcurl/lib/curl_get_line.h
index b2a534d001..0ff32c5c2c 100644
--- a/Utilities/cmcurl/lib/curl_get_line.h
+++ b/Utilities/cmcurl/lib/curl_get_line.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gethostname.c b/Utilities/cmcurl/lib/curl_gethostname.c
index 4747e938db..706b2e6892 100644
--- a/Utilities/cmcurl/lib/curl_gethostname.c
+++ b/Utilities/cmcurl/lib/curl_gethostname.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gethostname.h b/Utilities/cmcurl/lib/curl_gethostname.h
index b7360969ac..9281d9c242 100644
--- a/Utilities/cmcurl/lib/curl_gethostname.h
+++ b/Utilities/cmcurl/lib/curl_gethostname.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c
index 01ab48ec3e..c4c4f88ef9 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.c
+++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h
index b4ed482aa2..7b9a534ea2 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.h
+++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h
index 36c0bd62e5..11625c0cb4 100644
--- a/Utilities/cmcurl/lib/curl_hmac.h
+++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_krb5.h b/Utilities/cmcurl/lib/curl_krb5.h
index ccd6f10e3d..ccf6b96a87 100644
--- a/Utilities/cmcurl/lib/curl_krb5.h
+++ b/Utilities/cmcurl/lib/curl_krb5.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h
index ba3ede4260..8a1d807ed9 100644
--- a/Utilities/cmcurl/lib/curl_ldap.h
+++ b/Utilities/cmcurl/lib/curl_ldap.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_log.c b/Utilities/cmcurl/lib/curl_log.c
new file mode 100644
index 0000000000..b5ce1c7e09
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_log.c
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_log.h"
+#include "urldata.h"
+#include "easyif.h"
+#include "cfilters.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "strcase.h"
+
+#include "cf-socket.h"
+#include "connect.h"
+#include "http2.h"
+#include "http_proxy.h"
+#include "cf-http.h"
+#include "socks.h"
+#include "strtok.h"
+#include "vtls/vtls.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size)
+{
+ if(data->set.verbose) {
+ static const char s_infotype[CURLINFO_END][3] = {
+ "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+ if(data->set.fdebug) {
+ bool inCallback = Curl_is_in_callback(data);
+ Curl_set_in_callback(data, true);
+ (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
+ Curl_set_in_callback(data, inCallback);
+ }
+ else {
+ switch(type) {
+ case CURLINFO_TEXT:
+ case CURLINFO_HEADER_OUT:
+ case CURLINFO_HEADER_IN:
+ fwrite(s_infotype[type], 2, 1, data->set.err);
+ fwrite(ptr, size, 1, data->set.err);
+ break;
+ default: /* nada */
+ break;
+ }
+ }
+ }
+}
+
+
+/* Curl_failf() is for messages stating why we failed.
+ * The message SHALL NOT include any LF or CR.
+ */
+void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(data->set.verbose || data->set.errorbuffer) {
+ va_list ap;
+ int len;
+ char error[CURL_ERROR_SIZE + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+
+ if(data->set.errorbuffer && !data->state.errorbuf) {
+ strcpy(data->set.errorbuffer, error);
+ data->state.errorbuf = TRUE; /* wrote error string */
+ }
+ error[len++] = '\n';
+ error[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, error, len);
+ va_end(ap);
+ }
+}
+
+/* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
+
+void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(data && data->set.verbose) {
+ va_list ap;
+ int len;
+ char buffer[MAXINFO + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(buffer, MAXINFO, fmt, ap);
+ va_end(ap);
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ }
+}
+
+#ifdef DEBUGBUILD
+
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...)
+{
+ DEBUGASSERT(cf);
+ if(data && Curl_log_cf_is_debug(cf)) {
+ va_list ap;
+ int len;
+ char buffer[MAXINFO + 2];
+ len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ",
+ cf->conn->connection_id, cf->sockindex? "/2" : "",
+ cf->cft->name);
+ va_start(ap, fmt);
+ len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+ va_end(ap);
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ }
+}
+
+
+static struct Curl_cftype *cf_types[] = {
+ &Curl_cft_tcp,
+ &Curl_cft_udp,
+ &Curl_cft_unix,
+ &Curl_cft_tcp_accept,
+ &Curl_cft_happy_eyeballs,
+ &Curl_cft_setup,
+#ifdef USE_NGHTTP2
+ &Curl_cft_nghttp2,
+#endif
+#ifdef USE_SSL
+ &Curl_cft_ssl,
+ &Curl_cft_ssl_proxy,
+#endif
+#if !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP)
+ &Curl_cft_http_proxy,
+#endif /* !CURL_DISABLE_HTTP */
+ &Curl_cft_haproxy,
+ &Curl_cft_socks_proxy,
+#endif /* !CURL_DISABLE_PROXY */
+#ifdef ENABLE_QUIC
+ &Curl_cft_http3,
+#endif
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+ &Curl_cft_http_connect,
+#endif
+ NULL,
+};
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+CURLcode Curl_log_init(void)
+{
+ const char *setting = getenv("CURL_DEBUG");
+ if(setting) {
+ char *token, *tok_buf, *tmp;
+ size_t i;
+
+ tmp = strdup(setting);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ token = strtok_r(tmp, ", ", &tok_buf);
+ while(token) {
+ for(i = 0; cf_types[i]; ++i) {
+ if(strcasecompare(token, cf_types[i]->name)) {
+ cf_types[i]->log_level = CURL_LOG_DEBUG;
+ break;
+ }
+ }
+ token = strtok_r(NULL, ", ", &tok_buf);
+ }
+ free(tmp);
+ }
+ return CURLE_OK;
+}
+#else /* DEBUGBUILD */
+
+CURLcode Curl_log_init(void)
+{
+ return CURLE_OK;
+}
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...)
+{
+ (void)data;
+ (void)cf;
+ (void)fmt;
+}
+#endif
+
+#endif /* !DEBUGBUILD */
diff --git a/Utilities/cmcurl/lib/curl_log.h b/Utilities/cmcurl/lib/curl_log.h
new file mode 100644
index 0000000000..ad6143fa99
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_log.h
@@ -0,0 +1,138 @@
+#ifndef HEADER_CURL_LOG_H
+#define HEADER_CURL_LOG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+struct Curl_easy;
+struct Curl_cfilter;
+
+/**
+ * Init logging, return != 0 on failure.
+ */
+CURLcode Curl_log_init(void);
+
+
+void Curl_infof(struct Curl_easy *, const char *fmt, ...);
+void Curl_failf(struct Curl_easy *, const char *fmt, ...);
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+
+#if defined(HAVE_VARIADIC_MACROS_C99)
+#define infof(...) Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC)
+#define infof(x...) Curl_nop_stmt
+#else
+#error "missing VARIADIC macro define, fix and rebuild!"
+#endif
+
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define infof Curl_infof
+
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define failf Curl_failf
+
+
+#define CURL_LOG_DEFAULT 0
+#define CURL_LOG_DEBUG 1
+#define CURL_LOG_TRACE 2
+
+
+/* the function used to output verbose information */
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size);
+
+#ifdef DEBUGBUILD
+
+/* explainer: we have some mix configuration and werror settings
+ * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
+ * on gnuc and some other compiler. Need to treat carefully.
+ */
+#if defined(HAVE_VARIADIC_MACROS_C99) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+
+#define LOG_CF(data, cf, ...) \
+ do { if(Curl_log_cf_is_debug(cf)) \
+ Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0)
+#else
+#define LOG_CF Curl_log_cf_debug
+#endif
+
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+#if defined(__GNUC__) && !defined(printf) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+ !defined(__MINGW32__)
+ const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+#else
+ const char *fmt, ...);
+#endif
+
+#define Curl_log_cf_is_debug(cf) \
+ ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG)
+
+#else /* !DEBUGBUILD */
+
+#if defined(HAVE_VARIADIC_MACROS_C99) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define LOG_CF(...) Curl_nop_stmt
+#define Curl_log_cf_debug(...) Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define LOG_CF(x...) Curl_nop_stmt
+#define Curl_log_cf_debug(x...) Curl_nop_stmt
+#else
+#define LOG_CF Curl_log_cf_debug
+/* without c99, we seem unable to completely define away this function. */
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...);
+#endif
+
+#define Curl_log_cf_is_debug(x) ((void)(x), FALSE)
+
+#endif /* !DEBUGBUILD */
+
+#define LOG_CF_IS_DEBUG(x) Curl_log_cf_is_debug(x)
+
+/* Macros intended for DEBUGF logging, use like:
+ * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
+ * and it will output:
+ * [CONN-1-0][CF-SSL] this filter very much rocks
+ * on connection #1 with sockindex 0 for filter of type "SSL". */
+#define DMSG(d,msg) \
+ "[CONN-%ld] "msg, (d)->conn->connection_id
+#define DMSGI(d,i,msg) \
+ "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
+#define CMSG(c,msg) \
+ "[CONN-%ld] "msg, (c)->connection_id
+#define CMSGI(c,i,msg) \
+ "[CONN-%ld-%d] "msg, (c)->connection_id, (i)
+#define CFMSG(cf,msg) \
+ "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
+ (cf)->sockindex, (cf)->cft->name
+
+
+
+#endif /* HEADER_CURL_LOG_H */
diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h
index 8049355cff..03567b9916 100644
--- a/Utilities/cmcurl/lib/curl_md4.h
+++ b/Utilities/cmcurl/lib/curl_md4.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h
index 789329654b..ec2512f002 100644
--- a/Utilities/cmcurl/lib/curl_md5.h
+++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h
index 092fc9f758..7af139196e 100644
--- a/Utilities/cmcurl/lib/curl_memory.h
+++ b/Utilities/cmcurl/lib/curl_memory.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memrchr.c b/Utilities/cmcurl/lib/curl_memrchr.c
index c329a6176a..3f3dc6de16 100644
--- a/Utilities/cmcurl/lib/curl_memrchr.c
+++ b/Utilities/cmcurl/lib/curl_memrchr.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h
index e7654e1d9a..a1a4ba0927 100644
--- a/Utilities/cmcurl/lib/curl_memrchr.h
+++ b/Utilities/cmcurl/lib/curl_memrchr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c
index 309dccbfef..522ea34e82 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.c
+++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h
index 929714873a..ddac1f6382 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.h
+++ b/Utilities/cmcurl/lib/curl_multibyte.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index 690f8f7674..25d2526025 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,12 +36,13 @@
/* Please keep the SSL backend-specific #if branches in this order:
1. USE_OPENSSL
- 2. USE_GNUTLS
- 3. USE_NSS
- 4. USE_MBEDTLS
- 5. USE_SECTRANSP
- 6. USE_OS400CRYPTO
- 7. USE_WIN32_CRYPTO
+ 2. USE_WOLFSSL
+ 3. USE_GNUTLS
+ 4. USE_NSS
+ 5. USE_MBEDTLS
+ 6. USE_SECTRANSP
+ 7. USE_OS400CRYPTO
+ 8. USE_WIN32_CRYPTO
This ensures that:
- the same SSL branch gets activated throughout this source
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h
index 60444c9dc6..33b651f5ea 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,11 +37,11 @@
#define NTLM_NEEDS_NSS_INIT
#endif
-#ifdef USE_WOLFSSL
+#if defined(USE_OPENSSL)
+# include <openssl/ssl.h>
+#elif defined(USE_WOLFSSL)
# include <wolfssl/options.h>
# include <wolfssl/openssl/ssl.h>
-#elif defined(USE_OPENSSL)
-# include <openssl/ssl.h>
#endif
/* Helpers to generate function byte arguments in little endian order */
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index fcf5075227..a10e2a1b09 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.h b/Utilities/cmcurl/lib/curl_ntlm_wb.h
index 1f04db8c3d..37704c0fe0 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c
index f00e3ee74d..2df8687a55 100644
--- a/Utilities/cmcurl/lib/curl_path.c
+++ b/Utilities/cmcurl/lib/curl_path.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h
index 98e56eaad3..9ed09dea83 100644
--- a/Utilities/cmcurl/lib/curl_path.h
+++ b/Utilities/cmcurl/lib/curl_path.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h
index 3823828cd9..6d3d492cc4 100644
--- a/Utilities/cmcurl/lib/curl_printf.h
+++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_range.c b/Utilities/cmcurl/lib/curl_range.c
index 49999367d7..d499953c9e 100644
--- a/Utilities/cmcurl/lib/curl_range.c
+++ b/Utilities/cmcurl/lib/curl_range.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_range.h b/Utilities/cmcurl/lib/curl_range.h
index 33570abc25..77679e2b94 100644
--- a/Utilities/cmcurl/lib/curl_range.h
+++ b/Utilities/cmcurl/lib/curl_range.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c
index 1932cb4ecb..2679a2cdc1 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.c
+++ b/Utilities/cmcurl/lib/curl_rtmp.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h
index f856085dc3..9b93ee060b 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.h
+++ b/Utilities/cmcurl/lib/curl_rtmp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index 46ee800a43..119fb9b25a 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,7 +36,8 @@
#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 <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/curl_sasl.h b/Utilities/cmcurl/lib/curl_sasl.h
index c709d56a67..e94e6431a2 100644
--- a/Utilities/cmcurl/lib/curl_sasl.h
+++ b/Utilities/cmcurl/lib/curl_sasl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -125,9 +125,9 @@ struct SASL {
unsigned short authmechs; /* Accepted authentication mechanisms */
unsigned short prefmech; /* Preferred authentication mechanism */
unsigned short authused; /* Auth mechanism used for the connection */
- bool resetprefs; /* For URL auth option parsing. */
- bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */
- bool force_ir; /* Protocol always supports initial response */
+ BIT(resetprefs); /* For URL auth option parsing. */
+ BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */
+ BIT(force_ir); /* Protocol always supports initial response */
};
/* This is used to test whether the line starts with the given mechanism */
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 084d19a4e8..b58f813bf9 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -792,14 +792,16 @@ endings either CRLF or LF so 't' is appropriate.
#define FOPEN_APPENDTEXT "a"
#endif
-/* WinSock destroys recv() buffer when send() failed.
- * Enabled automatically for Windows and for Cygwin as Cygwin sockets are
- * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657
- * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround.
+/* Windows workaround to recv before every send, because apparently Winsock
+ * destroys destroys recv() buffer when send() failed.
+ * This workaround is now disabled by default since it caused hard to fix bugs.
+ * Define USE_RECV_BEFORE_SEND_WORKAROUND to enable it.
+ * https://github.com/curl/curl/issues/657
+ * https://github.com/curl/curl/pull/10409
*/
#if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND)
# if defined(WIN32) || defined(__CYGWIN__)
-# define USE_RECV_BEFORE_SEND_WORKAROUND
+/* # define USE_RECV_BEFORE_SEND_WORKAROUND */
# endif
#else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */
# ifdef USE_RECV_BEFORE_SEND_WORKAROUND
@@ -851,6 +853,13 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
#define USE_HTTP3
#endif
+/* Certain Windows implementations are not aligned with what curl expects,
+ so always use the local one on this platform. E.g. the mingw-w64
+ implementation can return wrong results for non-ASCII inputs. */
+#if defined(HAVE_BASENAME) && defined(WIN32)
+#undef HAVE_BASENAME
+#endif
+
#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
# if defined(__MINGW32__) && !defined(LUP_SECURE)
typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
@@ -868,4 +877,10 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
# endif
#endif
+/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no
+ replacements (yet) so tell the compiler to not warn for them. */
+#ifdef USE_OPENSSL
+#define OPENSSL_SUPPRESS_DEPRECATED
+#endif
+
#endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h
index ac4a7f1b35..e68eb502a0 100644
--- a/Utilities/cmcurl/lib/curl_setup_once.h
+++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h
index 39523af0c8..c5e157bee1 100644
--- a/Utilities/cmcurl/lib/curl_sha256.h
+++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c
index 33108c48e9..eb21e7e2b0 100644
--- a/Utilities/cmcurl/lib/curl_sspi.c
+++ b/Utilities/cmcurl/lib/curl_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h
index ad111309c5..9816d59c84 100644
--- a/Utilities/cmcurl/lib/curl_sspi.h
+++ b/Utilities/cmcurl/lib/curl_sspi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c
index dff61676c9..e13e2947c2 100644
--- a/Utilities/cmcurl/lib/curl_threads.c
+++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h
index 63392f671f..facbc73705 100644
--- a/Utilities/cmcurl/lib/curl_threads.h
+++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h
index 1796afa00a..7a753d6824 100644
--- a/Utilities/cmcurl/lib/curlx.h
+++ b/Utilities/cmcurl/lib/curlx.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index 993373ecae..0ce62a00e9 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -98,37 +98,27 @@ const struct Curl_handler Curl_handler_dict = {
PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
};
-static char *unescape_word(const char *inputbuff)
+#define DYN_DICT_WORD 10000
+static char *unescape_word(const char *input)
{
- char *newp = NULL;
- char *dictp;
- size_t len;
-
- CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len,
- REJECT_NADA);
- if(!newp || result)
- return NULL;
-
- dictp = malloc(len*2 + 1); /* add one for terminating zero */
- if(dictp) {
- char *ptr;
- char ch;
- int olen = 0;
- /* According to RFC2229 section 2.2, these letters need to be escaped with
- \[letter] */
- for(ptr = newp;
- (ch = *ptr) != 0;
- ptr++) {
- if((ch <= 32) || (ch == 127) ||
- (ch == '\'') || (ch == '\"') || (ch == '\\')) {
- dictp[olen++] = '\\';
- }
- dictp[olen++] = ch;
- }
- dictp[olen] = 0;
+ struct dynbuf out;
+ const char *ptr;
+ CURLcode result = CURLE_OK;
+ Curl_dyn_init(&out, DYN_DICT_WORD);
+
+ /* According to RFC2229 section 2.2, these letters need to be escaped with
+ \[letter] */
+ for(ptr = input; *ptr; ptr++) {
+ char ch = *ptr;
+ if((ch <= 32) || (ch == 127) ||
+ (ch == '\'') || (ch == '\"') || (ch == '\\'))
+ result = Curl_dyn_addn(&out, "\\", 1);
+ if(!result)
+ result = Curl_dyn_addn(&out, ptr, 1);
+ if(result)
+ return NULL;
}
- free(newp);
- return dictp;
+ return Curl_dyn_ptr(&out);
}
/* sendf() sends formatted data to the server */
@@ -178,20 +168,25 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
static CURLcode dict_do(struct Curl_easy *data, bool *done)
{
char *word;
- char *eword;
+ char *eword = NULL;
char *ppath;
char *database = NULL;
char *strategy = NULL;
char *nthdef = NULL; /* This is not part of the protocol, but required
by RFC 2229 */
- CURLcode result = CURLE_OK;
+ CURLcode result;
struct connectdata *conn = data->conn;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
- char *path = data->state.up.path;
+ char *path;
*done = TRUE; /* unconditionally */
+ /* url-decode path before further evaluation */
+ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
+ if(result)
+ return result;
+
if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
@@ -225,8 +220,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
}
eword = unescape_word(word);
- if(!eword)
- return CURLE_OUT_OF_MEMORY;
+ if(!eword) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
result = sendf(sockfd, data,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
@@ -239,11 +236,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
strategy,
eword);
- free(eword);
-
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
}
@@ -273,8 +268,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
}
eword = unescape_word(word);
- if(!eword)
- return CURLE_OUT_OF_MEMORY;
+ if(!eword) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
result = sendf(sockfd, data,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
@@ -285,11 +282,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
database,
eword);
- free(eword);
-
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
@@ -310,13 +305,16 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
"QUIT\r\n", ppath);
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
}
- return CURLE_OK;
+ error:
+ free(eword);
+ free(path);
+ return result;
}
#endif /* CURL_DISABLE_DICT */
diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h
index b283a0dfc1..ba9a92719b 100644
--- a/Utilities/cmcurl/lib/dict.h
+++ b/Utilities/cmcurl/lib/dict.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index 3b1d5d60ef..2e6a37785c 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -396,6 +396,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
goto error;
dohp->pending++;
+#ifdef ENABLE_IPV6
if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* create IPv6 DoH request */
result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
@@ -405,6 +406,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
goto error;
dohp->pending++;
}
+#endif
return NULL;
error:
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h
index 678e807fe4..7d7b694f33 100644
--- a/Utilities/cmcurl/lib/doh.h
+++ b/Utilities/cmcurl/lib/doh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c
index 0b1cf9afd8..847f6f6ad1 100644
--- a/Utilities/cmcurl/lib/dynbuf.c
+++ b/Utilities/cmcurl/lib/dynbuf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h
index 04a728c779..57ad62b22b 100644
--- a/Utilities/cmcurl/lib/dynbuf.h
+++ b/Utilities/cmcurl/lib/dynbuf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index d7f93be1e0..db03026c95 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -65,6 +65,7 @@
#include "easyif.h"
#include "multiif.h"
#include "select.h"
+#include "cfilters.h"
#include "sendf.h" /* for failf function prototype */
#include "connect.h" /* for Curl_getconnectinfo */
#include "slist.h"
@@ -113,7 +114,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
#if defined(_WIN32_WCE)
#define system_strdup _strdup
#elif !defined(HAVE_STRDUP)
-#define system_strdup curlx_strdup
+#define system_strdup Curl_strdup
#else
#define system_strdup strdup
#endif
@@ -164,6 +165,11 @@ static CURLcode global_init(long flags, bool memoryfuncs)
#endif
}
+ if(Curl_log_init()) {
+ DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n"));
+ goto fail;
+ }
+
if(!Curl_ssl_init()) {
DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
goto fail;
@@ -913,11 +919,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
goto fail;
}
- /* duplicate all values in 'change' */
- if(data->state.cookielist) {
- outcurl->state.cookielist =
- Curl_slist_duplicate(data->state.cookielist);
- if(!outcurl->state.cookielist)
+ if(data->set.cookielist) {
+ outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist);
+ if(!outcurl->set.cookielist)
goto fail;
}
#endif
@@ -1003,8 +1007,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
if(outcurl) {
#ifndef CURL_DISABLE_COOKIES
- curl_slist_free_all(outcurl->state.cookielist);
- outcurl->state.cookielist = NULL;
+ curl_slist_free_all(outcurl->set.cookielist);
+ outcurl->set.cookielist = NULL;
#endif
Curl_safefree(outcurl->state.buffer);
Curl_dyn_free(&outcurl->state.headerb);
@@ -1101,7 +1105,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
k->keepon = newstate;
if(!(newstate & KEEP_RECV_PAUSE)) {
- Curl_http2_stream_pause(data, FALSE);
+ Curl_conn_ev_data_pause(data, FALSE);
if(data->state.tempcount) {
/* there are buffers for sending that can be delivered as the receive
@@ -1224,7 +1228,7 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
return result;
*n = (size_t)n1;
-
+ infof(data, "reached %s:%d", __FILE__, __LINE__);
return CURLE_OK;
}
@@ -1294,29 +1298,34 @@ static int conn_upkeep(struct Curl_easy *data,
struct connectdata *conn,
void *param)
{
- /* Param is unused. */
- (void)param;
+ struct curltime *now = param;
- if(conn->handler->connection_check) {
- /* briefly attach the connection to this transfer for the purpose of
- checking it */
- Curl_attach_connection(data, conn);
+ if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+ return 0;
+ /* briefly attach for action */
+ Curl_attach_connection(data, conn);
+ if(conn->handler->connection_check) {
/* Do a protocol-specific keepalive check on the connection. */
conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
- /* detach the connection again */
- Curl_detach_connection(data);
}
+ else {
+ /* Do the generic action on the FIRSTSOCKE filter chain */
+ Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
+ }
+ Curl_detach_connection(data);
+ conn->keepalive = *now;
return 0; /* continue iteration */
}
static CURLcode upkeep(struct conncache *conn_cache, void *data)
{
+ struct curltime now = Curl_now();
/* Loop over every connection and make connection alive. */
Curl_conncache_foreach(data,
conn_cache,
- data,
+ &now,
conn_upkeep);
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
index d96e56b8d8..5fa9477d06 100644
--- a/Utilities/cmcurl/lib/easy_lock.h
+++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c
index a639bb3758..2b8a521cd2 100644
--- a/Utilities/cmcurl/lib/easygetopt.c
+++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* ___|___/|_| ______|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h
index 205382cd07..570ebef326 100644
--- a/Utilities/cmcurl/lib/easyif.h
+++ b/Utilities/cmcurl/lib/easyif.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c
index 03b814e5aa..a9c1efd006 100644
--- a/Utilities/cmcurl/lib/easyoptions.c
+++ b/Utilities/cmcurl/lib/easyoptions.c
@@ -1,11 +1,11 @@
/***************************************************************************
* _ _ ____ _
- * Project ___| | | | _ | |
+ * Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
- * ___|___/|_| ______|
+ * \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyoptions.h b/Utilities/cmcurl/lib/easyoptions.h
index 33f816d6b6..24b4cd93ed 100644
--- a/Utilities/cmcurl/lib/easyoptions.h
+++ b/Utilities/cmcurl/lib/easyoptions.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c
index ed59838bd1..56aa2b3988 100644
--- a/Utilities/cmcurl/lib/escape.c
+++ b/Utilities/cmcurl/lib/escape.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -97,7 +97,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
return strdup("");
while(length--) {
- unsigned char in = *string; /* we need to treat the characters unsigned */
+ unsigned char in = *string++; /* treat the characters unsigned */
if(Curl_isunreserved(in)) {
/* append this */
@@ -106,15 +106,28 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
}
else {
/* encode it */
- if(Curl_dyn_addf(&d, "%%%02X", in))
+ const char hex[] = "0123456789ABCDEF";
+ char out[3]={'%'};
+ out[1] = hex[in>>4];
+ out[2] = hex[in & 0xf];
+ if(Curl_dyn_addn(&d, out, 3))
return NULL;
}
- string++;
}
return Curl_dyn_ptr(&d);
}
+static const unsigned char hextable[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
+ 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */
+};
+
+/* the input is a single hex digit */
+#define onehex2dec(x) hextable[x - '0']
+
/*
* Curl_urldecode() URL decodes the given string.
*
@@ -137,54 +150,47 @@ CURLcode Curl_urldecode(const char *string, size_t length,
{
size_t alloc;
char *ns;
- size_t strindex = 0;
- unsigned long hex;
DEBUGASSERT(string);
DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
- alloc = (length?length:strlen(string)) + 1;
- ns = malloc(alloc);
+ alloc = (length?length:strlen(string));
+ ns = malloc(alloc + 1);
if(!ns)
return CURLE_OUT_OF_MEMORY;
- while(--alloc > 0) {
+ /* store output string */
+ *ostring = ns;
+
+ while(alloc) {
unsigned char in = *string;
if(('%' == in) && (alloc > 2) &&
ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
/* this is two hexadecimal digits following a '%' */
- char hexstr[3];
- char *ptr;
- hexstr[0] = string[1];
- hexstr[1] = string[2];
- hexstr[2] = 0;
-
- hex = strtoul(hexstr, &ptr, 16);
+ in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]);
- in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
-
- string += 2;
- alloc -= 2;
+ string += 3;
+ alloc -= 3;
+ }
+ else {
+ string++;
+ alloc--;
}
if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
((ctrl == REJECT_ZERO) && (in == 0))) {
- free(ns);
+ Curl_safefree(*ostring);
return CURLE_URL_MALFORMAT;
}
- ns[strindex++] = in;
- string++;
+ *ns++ = in;
}
- ns[strindex] = 0; /* terminate it */
+ *ns = 0; /* terminate it */
if(olen)
/* store output size */
- *olen = strindex;
-
- /* store output string */
- *ostring = ns;
+ *olen = ns - *ostring;
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h
index 61d4611bf7..cdbb712acc 100644
--- a/Utilities/cmcurl/lib/escape.h
+++ b/Utilities/cmcurl/lib/escape.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index 3f642be461..51c5d07ce4 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h
index 826d45380d..4565525592 100644
--- a/Utilities/cmcurl/lib/file.h
+++ b/Utilities/cmcurl/lib/file.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fileinfo.c b/Utilities/cmcurl/lib/fileinfo.c
index 7bbf24bdeb..409b38fcbc 100644
--- a/Utilities/cmcurl/lib/fileinfo.c
+++ b/Utilities/cmcurl/lib/fileinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fileinfo.h b/Utilities/cmcurl/lib/fileinfo.h
index 5bad718cc8..af44212501 100644
--- a/Utilities/cmcurl/lib/fileinfo.h
+++ b/Utilities/cmcurl/lib/fileinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c
index ad3691ba9d..f710dbf05a 100644
--- a/Utilities/cmcurl/lib/fopen.c
+++ b/Utilities/cmcurl/lib/fopen.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -106,7 +106,6 @@ fail:
free(tempstore);
- *tempname = NULL;
return result;
}
diff --git a/Utilities/cmcurl/lib/fopen.h b/Utilities/cmcurl/lib/fopen.h
index 289e55f2af..e3a919d073 100644
--- a/Utilities/cmcurl/lib/fopen.h
+++ b/Utilities/cmcurl/lib/fopen.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index b30e8deee4..2bdb9f26ec 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h
index c6c6397cd2..caabb6324c 100644
--- a/Utilities/cmcurl/lib/formdata.h
+++ b/Utilities/cmcurl/lib/formdata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 874725bae3..8c728745d9 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -61,6 +61,7 @@
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
+#include "cf-socket.h"
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
@@ -285,8 +286,8 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
conn->bits.do_more = FALSE;
(void)curlx_nonblock(s, TRUE); /* enable non-blocking */
- /* Replace any filter on SECONDARY with one listeing on this socket */
- result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s);
+ /* Replace any filter on SECONDARY with one listening on this socket */
+ result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
if(result)
return result;
@@ -819,26 +820,11 @@ static int ftp_domore_getsock(struct Curl_easy *data,
if(FTP_STOP == ftpc->state) {
int bits = GETSOCK_READSOCK(0);
- bool any = FALSE;
/* if stopped and still in this state, then we're also waiting for a
connect on the secondary connection */
socks[0] = conn->sock[FIRSTSOCKET];
-
- if(!data->set.ftp_use_port) {
- int s;
- int i;
- /* PORT is used to tell the server to connect to us, and during that we
- don't do happy eyeballs, but we do if we connect to the server */
- for(s = 1, i = 0; i<2; i++) {
- if(conn->tempsock[i] != CURL_SOCKET_BAD) {
- socks[s] = conn->tempsock[i];
- bits |= GETSOCK_WRITESOCK(s++);
- any = TRUE;
- }
- }
- }
- if(!any) {
+ if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
socks[1] = conn->sock[SECONDARYSOCKET];
bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
}
@@ -1024,9 +1010,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(*addr != '\0') {
/* attempt to get the address of the given interface name */
- switch(Curl_if2ip(conn->ip_addr->ai_family,
+ switch(Curl_if2ip(conn->remote_addr->family,
#ifdef ENABLE_IPV6
- Curl_ipv6_scope(conn->ip_addr->ai_addr),
+ Curl_ipv6_scope(&conn->remote_addr->sa_addr),
conn->scope_id,
#endif
addr, hbuf, sizeof(hbuf))) {
@@ -1097,7 +1083,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
portsock = CURL_SOCKET_BAD;
error = 0;
for(ai = res; ai; ai = ai->ai_next) {
- if(Curl_socket(data, ai, NULL, &portsock)) {
+ if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
error = SOCKERRNO;
continue;
}
@@ -1266,9 +1252,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */
ftpc->count1 = fcmd;
- /* Replace any filter on SECONDARY with one listeing on this socket */
- result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET,
- &portsock);
+ /* Replace any filter on SECONDARY with one listening on this socket */
+ result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
if(result)
goto out;
portsock = CURL_SOCKET_BAD; /* now held in filter */
@@ -1279,7 +1264,7 @@ out:
state(data, FTP_STOP);
}
if(portsock != CURL_SOCKET_BAD)
- Curl_closesocket(data, conn, portsock);
+ Curl_socket_close(data, conn, portsock);
free(addr);
return result;
}
@@ -1954,7 +1939,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
- Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]);
+ Curl_conn_ev_update_info(data, conn);
Curl_safefree(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
if(!ftpc->newhost)
@@ -2569,13 +2554,11 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data)
/* for USER and PASS responses */
static CURLcode ftp_state_user_resp(struct Curl_easy *data,
- int ftpcode,
- ftpstate instate)
+ int ftpcode)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- (void)instate; /* no use for this yet */
/* some need password anyway, and others just return 2xx ignored */
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
@@ -2670,7 +2653,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
/* 230 User logged in - already! Take as 220 if TLS required. */
if(data->set.use_ssl <= CURLUSESSL_TRY ||
conn->bits.ftp_use_control_ssl)
- return ftp_state_user_resp(data, ftpcode, ftpc->state);
+ return ftp_state_user_resp(data, ftpcode);
}
else if(ftpcode != 220) {
failf(data, "Got a %03d ftp-server response when 220 was expected",
@@ -2742,7 +2725,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
if((ftpcode == 234) || (ftpcode == 334)) {
/* this was BLOCKING, keep it so for now */
bool done;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result) {
/* we failed and bail out */
@@ -2775,7 +2758,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
case FTP_USER:
case FTP_PASS:
- result = ftp_state_user_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_user_resp(data, ftpcode);
break;
case FTP_ACCT:
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h
index 7f6f4328d1..65efa6f941 100644
--- a/Utilities/cmcurl/lib/ftp.h
+++ b/Utilities/cmcurl/lib/ftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,7 +42,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread,
/****************************************************************************
* FTP unique setup
***************************************************************************/
-typedef enum {
+enum {
FTP_STOP, /* do nothing state, stops the state machine */
FTP_WAIT220, /* waiting for the initial 220 response immediately after
a connect */
@@ -80,7 +80,8 @@ typedef enum {
FTP_STOR, /* generic state for STOR and APPE */
FTP_QUIT,
FTP_LAST /* never used */
-} ftpstate;
+};
+typedef unsigned char ftpstate; /* use the enum values */
struct ftp_parselist_data; /* defined later in ftplistparser.c */
@@ -122,38 +123,38 @@ struct ftp_conn {
char *entrypath; /* the PWD reply when we logged on */
char *file; /* url-decoded file name (or path) */
char **dirs; /* realloc()ed array for path components */
- int dirdepth; /* number of entries used in the 'dirs' array */
- bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
- file size and 226/250 status check. It should still
- read the line, just ignore the result. */
- bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
- the connection has timed out or been closed, this
- should be FALSE when it gets to Curl_ftp_quit() */
- bool cwddone; /* if it has been determined that the proper CWD combo
- already has been done */
- int cwdcount; /* number of CWD commands issued */
- bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
- caching the current directory */
- bool wait_data_conn; /* this is set TRUE if data connection is waited */
- /* newhost is the (allocated) IP addr or host name to connect the data
- connection to */
- unsigned short newport;
char *newhost;
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) */
- int count1; /* general purpose counter for the state machine */
- int count2; /* general purpose counter for the state machine */
- int count3; /* general purpose counter for the state machine */
- ftpstate state; /* always use ftp.c:state() to change state! */
- ftpstate state_saved; /* transfer type saved to be reloaded after
- data connection is established */
curl_off_t retr_size_saved; /* Size of retrieved file saved */
char *server_os; /* The target server operating system. */
curl_off_t known_filesize; /* file size is different from -1, if wildcard
LIST parsing was done and wc_statemach set
it */
+ int dirdepth; /* number of entries used in the 'dirs' array */
+ int cwdcount; /* number of CWD commands issued */
+ int count1; /* general purpose counter for the state machine */
+ int count2; /* general purpose counter for the state machine */
+ int count3; /* general purpose counter for the state machine */
+ /* newhost is the (allocated) IP addr or host name to connect the data
+ connection to */
+ unsigned short newport;
+ ftpstate state; /* always use ftp.c:state() to change state! */
+ ftpstate state_saved; /* transfer type saved to be reloaded after data
+ connection is established */
BIT(ftp_trying_alternative);
+ BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer)
+ file size and 226/250 status check. It should still
+ read the line, just ignore the result. */
+ BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If
+ the connection has timed out or been closed, this
+ should be FALSE when it gets to Curl_ftp_quit() */
+ BIT(cwddone); /* if it has been determined that the proper CWD combo
+ already has been done */
+ BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
+ caching the current directory */
+ BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
};
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index 3d529ef236..f7c6434eb0 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h
index 0a80543417..3d9a43b0df 100644
--- a/Utilities/cmcurl/lib/ftplistparser.h
+++ b/Utilities/cmcurl/lib/ftplistparser.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h
index 8891b1d5d6..075c02e54f 100644
--- a/Utilities/cmcurl/lib/functypes.h
+++ b/Utilities/cmcurl/lib/functypes.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c
index 5f00fd13a4..8069784728 100644
--- a/Utilities/cmcurl/lib/getenv.c
+++ b/Utilities/cmcurl/lib/getenv.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index 3a24c65294..826ffd0b0c 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getinfo.h b/Utilities/cmcurl/lib/getinfo.h
index 1b5e8c20f8..56bb440b43 100644
--- a/Utilities/cmcurl/lib/getinfo.h
+++ b/Utilities/cmcurl/lib/getinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index 6fbb7de327..4a11d9364e 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h
index 4ea269d2b4..9e3365b71c 100644
--- a/Utilities/cmcurl/lib/gopher.h
+++ b/Utilities/cmcurl/lib/gopher.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/h2h3.c b/Utilities/cmcurl/lib/h2h3.c
index 3a9288df5c..3b21699c3c 100644
--- a/Utilities/cmcurl/lib/h2h3.c
+++ b/Utilities/cmcurl/lib/h2h3.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -118,6 +118,7 @@ static header_instruction inspect_header(const char *name, size_t namelen,
CURLcode Curl_pseudo_headers(struct Curl_easy *data,
const char *mem, /* the request */
const size_t len /* size of request */,
+ size_t* hdrlen /* opt size of headers read */,
struct h2h3req **hp)
{
struct connectdata *conn = data->conn;
@@ -291,6 +292,12 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data,
}
}
+ if(hdrlen) {
+ /* Skip trailing CRLF */
+ end += 4;
+ *hdrlen = end - mem;
+ }
+
hreq->entries = nheader;
*hp = hreq;
diff --git a/Utilities/cmcurl/lib/h2h3.h b/Utilities/cmcurl/lib/h2h3.h
index c35b7061fa..396c12c625 100644
--- a/Utilities/cmcurl/lib/h2h3.h
+++ b/Utilities/cmcurl/lib/h2h3.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -51,6 +51,7 @@ struct h2h3req {
CURLcode Curl_pseudo_headers(struct Curl_easy *data,
const char *request,
const size_t len,
+ size_t* hdrlen /* optional */,
struct h2h3req **hp);
/*
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c
index b6a2a33c72..06ce92cbd5 100644
--- a/Utilities/cmcurl/lib/hash.c
+++ b/Utilities/cmcurl/lib/hash.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h
index 5b59bf1118..9cfffc25b0 100644
--- a/Utilities/cmcurl/lib/hash.h
+++ b/Utilities/cmcurl/lib/hash.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c
index 978c918f4f..22e0e01258 100644
--- a/Utilities/cmcurl/lib/headers.c
+++ b/Utilities/cmcurl/lib/headers.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h
index 96332dbd0b..a5229ea22f 100644
--- a/Utilities/cmcurl/lib/headers.h
+++ b/Utilities/cmcurl/lib/headers.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c
index dfb0db5757..8d8de1757d 100644
--- a/Utilities/cmcurl/lib/hmac.c
+++ b/Utilities/cmcurl/lib/hmac.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c
index df50d13e19..536eb731c4 100644
--- a/Utilities/cmcurl/lib/hostasyn.c
+++ b/Utilities/cmcurl/lib/hostasyn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index dd427a2c66..9738806b26 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index 3b1d814dab..e0d13cd402 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c
index 109bd1e5ea..9140180ffd 100644
--- a/Utilities/cmcurl/lib/hostip4.c
+++ b/Utilities/cmcurl/lib/hostip4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index af8bc23021..6b0ba55e9f 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c
index 73d1e50569..ca8b0758c4 100644
--- a/Utilities/cmcurl/lib/hostsyn.c
+++ b/Utilities/cmcurl/lib/hostsyn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index c449120f31..64cbae10c9 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,6 +39,7 @@
#include "parsedate.h"
#include "fopen.h"
#include "rename.h"
+#include "share.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -425,14 +426,23 @@ static CURLcode hsts_add(struct hsts *h, char *line)
if(2 == rc) {
time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
TIME_T_MAX;
- CURLcode result;
+ CURLcode result = CURLE_OK;
char *p = host;
bool subdomain = FALSE;
+ struct stsentry *e;
if(p[0] == '.') {
p++;
subdomain = TRUE;
}
- result = hsts_create(h, p, subdomain, expires);
+ /* only add it if not already present */
+ e = Curl_hsts(h, p, subdomain);
+ if(!e)
+ result = hsts_create(h, p, subdomain, expires);
+ else {
+ /* the same host name, use the largest expire time */
+ if(expires > e->expires)
+ e->expires = expires;
+ }
if(result)
return result;
}
@@ -551,4 +561,18 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
return CURLE_OK;
}
+void Curl_hsts_loadfiles(struct Curl_easy *data)
+{
+ struct curl_slist *l = data->set.hstslist;
+ if(l) {
+ Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
+
+ while(l) {
+ (void)Curl_hsts_loadfile(data, data->hsts, l->data);
+ l = l->next;
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_HSTS);
+ }
+}
+
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h
index 0e36a7756b..d3431a5d7a 100644
--- a/Utilities/cmcurl/lib/hsts.h
+++ b/Utilities/cmcurl/lib/hsts.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,9 +59,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
struct hsts *h, const char *file);
CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
struct hsts *h);
+void Curl_hsts_loadfiles(struct Curl_easy *data);
#else
#define Curl_hsts_cleanup(x)
#define Curl_hsts_loadcb(x,y) CURLE_OK
#define Curl_hsts_save(x,y,z)
+#define Curl_hsts_loadfiles(x)
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
#endif /* HEADER_CURL_HSTS_H */
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 1b7502280e..cb585e72fd 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,6 +62,7 @@
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
+#include "vquic/vquic.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "curl_ntlm_wb.h"
@@ -150,7 +151,7 @@ const struct Curl_handler Curl_handler_ws = {
http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ZERO_NULL, /* disconnect */
+ Curl_ws_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -204,7 +205,7 @@ const struct Curl_handler Curl_handler_wss = {
http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ZERO_NULL, /* disconnect */
+ Curl_ws_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -218,41 +219,6 @@ const struct Curl_handler Curl_handler_wss = {
#endif
-static CURLcode h3_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
-{
-#ifdef ENABLE_QUIC
- /* We want HTTP/3 directly, setup the filter chain ourself,
- * overriding the default behaviour. */
- DEBUGASSERT(conn->transport == TRNSPRT_QUIC);
-
- if(!(conn->handler->flags & PROTOPT_SSL)) {
- failf(data, "HTTP/3 requested for non-HTTPS URL");
- return CURLE_URL_MALFORMAT;
- }
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy) {
- 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 a HTTP proxy");
- return CURLE_URL_MALFORMAT;
- }
-#endif
-
- DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)",
- conn->connection_id, FIRSTSOCKET));
- return Curl_conn_socket_set(data, conn, FIRSTSOCKET);
-
-#else /* ENABLE_QUIC */
- (void)conn;
- (void)data;
- DEBUGF(infof(data, "QUIC is not supported in this build"));
- return CURLE_NOT_BUILT_IN;
-#endif /* !ENABLE_QUIC */
-}
-
static CURLcode http_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
@@ -268,19 +234,16 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
Curl_mime_initpart(&http->form);
data->req.p.http = http;
- if(data->state.httpwant == CURL_HTTP_VERSION_3) {
+ if((data->state.httpwant == CURL_HTTP_VERSION_3)
+ || (data->state.httpwant == CURL_HTTP_VERSION_3ONLY)) {
+ CURLcode result = Curl_conn_may_http3(data, conn);
+ if(result)
+ return result;
+
+ /* TODO: HTTP lower version eyeballing */
conn->transport = TRNSPRT_QUIC;
}
- if(conn->transport == TRNSPRT_QUIC) {
- return h3_setup_conn(data, conn);
- }
- else {
- if(!CONN_INUSE(conn))
- /* if not already multi-using, setup connection details */
- Curl_http2_setup_conn(conn);
- Curl_http2_setup_req(data);
- }
return CURLE_OK;
}
@@ -1256,8 +1219,8 @@ static size_t readmoredata(char *buffer,
size_t nitems,
void *userp)
{
- struct Curl_easy *data = (struct Curl_easy *)userp;
- struct HTTP *http = data->req.p.http;
+ struct HTTP *http = (struct HTTP *)userp;
+ struct Curl_easy *data = http->backup.data;
size_t fullsize = size * nitems;
if(!http->postsize)
@@ -1309,6 +1272,7 @@ static size_t readmoredata(char *buffer,
*/
CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data,
+ struct HTTP *http,
/* add the number of sent bytes to this
counter */
curl_off_t *bytes_written,
@@ -1321,14 +1285,13 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
char *ptr;
size_t size;
struct connectdata *conn = data->conn;
- struct HTTP *http = data->req.p.http;
size_t sendsize;
curl_socket_t sockfd;
size_t headersize;
DEBUGASSERT(socketindex <= SECONDARYSOCKET);
- sockfd = conn->sock[socketindex];
+ sockfd = Curl_conn_get_socket(data, socketindex);
/* The looping below is required since we use non-blocking sockets, but due
to the circumstances we will just loop and try again and again etc */
@@ -1456,10 +1419,11 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
http->backup.fread_in = data->state.in;
http->backup.postdata = http->postdata;
http->backup.postsize = http->postsize;
+ http->backup.data = data;
/* set the new pointers for the request-sending */
data->state.fread_func = (curl_read_callback)readmoredata;
- data->state.in = (void *)data;
+ data->state.in = (void *)http;
http->postdata = ptr;
http->postsize = (curl_off_t)size;
@@ -1468,7 +1432,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
http->send_buffer = *in; /* copy the whole struct */
http->sending = HTTPSEND_REQUEST;
-
return CURLE_OK;
}
http->sending = HTTPSEND_BODY;
@@ -1579,8 +1542,8 @@ static int http_getsock_do(struct Curl_easy *data,
curl_socket_t *socks)
{
/* write mode */
- (void)data;
- socks[0] = conn->sock[FIRSTSOCKET];
+ (void)conn;
+ socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET);
return GETSOCK_WRITESOCK(0);
}
@@ -1610,8 +1573,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;
Curl_dyn_free(&http->send_buffer);
- Curl_http2_done(data, premature);
- Curl_quic_done(data, premature);
Curl_mime_cleanpart(&http->form);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
@@ -1664,17 +1625,10 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data,
static const char *get_http_string(const struct Curl_easy *data,
const struct connectdata *conn)
{
-#ifdef ENABLE_QUIC
- if((data->state.httpwant == CURL_HTTP_VERSION_3) ||
- (conn->httpversion == 30))
+ if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
return "3";
-#endif
-
-#ifdef USE_NGHTTP2
- if(conn->proto.httpc.h2)
+ if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
return "2";
-#endif
-
if(Curl_use_http_1_1plus(data, conn))
return "1.1";
@@ -2359,7 +2313,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
curl_off_t included_body = 0;
#else
/* from this point down, this function should not be used */
-#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
+#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK
#endif
CURLcode result = CURLE_OK;
struct HTTP *http = data->req.p.http;
@@ -2403,7 +2357,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
Curl_pgrsSetUploadSize(data, http->postsize);
/* this sends the buffer and frees all the buffer resources */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending PUT request");
@@ -2424,7 +2379,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
if(result)
return result;
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
@@ -2440,8 +2396,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if(http->postsize != -1 && !data->req.upload_chunky &&
- (conn->bits.authneg ||
- !Curl_checkheaders(data, STRCONST("Content-Length")))) {
+ (!Curl_checkheaders(data, STRCONST("Content-Length")))) {
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
result = Curl_dyn_addf(r,
@@ -2495,7 +2450,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
http->sending = HTTPSEND_BODY;
/* this sends the buffer and frees all the buffer resources */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
@@ -2561,7 +2517,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
/* In HTTP2, we send request body in DATA frame regardless of
its size. */
- if(conn->httpversion != 20 &&
+ if(conn->httpversion < 20 &&
!data->state.expect100header &&
(http->postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
@@ -2612,11 +2568,10 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
else {
/* A huge POST coming up, do data separate from the request */
http->postdata = data->set.postfields;
-
http->sending = HTTPSEND_BODY;
-
+ http->backup.data = data;
data->state.fread_func = (curl_read_callback)readmoredata;
- data->state.in = (void *)data;
+ data->state.in = (void *)http;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
@@ -2655,7 +2610,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
}
/* issue the request */
- result = Curl_buffer_send(r, data, &data->info.request_size, included_body,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, included_body,
FIRSTSOCKET);
if(result)
@@ -2671,7 +2627,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
/* issue the request */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending HTTP request");
@@ -3021,50 +2978,27 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
the rest of the request in the PERFORM phase. */
*done = TRUE;
- if(conn->transport != TRNSPRT_QUIC) {
- if(conn->httpversion < 20) { /* unless the connection is re-used and
- already http2 */
- switch(conn->alpn) {
- case CURL_HTTP_VERSION_2:
- conn->httpversion = 20; /* we know we're on HTTP/2 now */
-
- result = Curl_http2_switched(data, NULL, 0);
- if(result)
- return result;
- break;
- case CURL_HTTP_VERSION_1_1:
- /* continue with HTTP/1.1 when explicitly requested */
- break;
- default:
- /* Check if user wants to use HTTP/2 with clear TCP */
-#ifdef USE_NGHTTP2
- if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
- /* We don't support HTTP/2 proxies yet. Also it's debatable
- whether or not this setting should apply to HTTP/2 proxies. */
- infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
- break;
- }
-#endif
- DEBUGF(infof(data, "HTTP/2 over clean TCP"));
- conn->httpversion = 20;
-
- result = Curl_http2_switched(data, NULL, 0);
- if(result)
- return result;
- }
-#endif
- break;
- }
- }
- else {
- /* prepare for an http2 request */
- result = Curl_http2_setup(data, conn);
+ switch(conn->alpn) {
+ case CURL_HTTP_VERSION_3:
+ DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
+ break;
+ case CURL_HTTP_VERSION_2:
+ DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ /* continue with HTTP/1.1 when explicitly requested */
+ break;
+ default:
+ /* Check if user wants to use HTTP/2 with clear TCP */
+ if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
+ DEBUGF(infof(data, "HTTP/2 over clean TCP"));
+ result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
return result;
}
+ break;
}
+
http = data->req.p.http;
DEBUGASSERT(http);
@@ -3224,7 +3158,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
- conn->httpversion != 20 &&
+ conn->httpversion < 20 &&
(data->state.httpwant == CURL_HTTP_VERSION_2)) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
over SSL */
@@ -3236,8 +3170,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
result = Curl_http_cookies(data, conn, &req);
+#ifdef USE_WEBSOCKETS
if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
result = Curl_ws_request(data, &req);
+#endif
if(!result)
result = Curl_add_timecondition(data, &req);
if(!result)
@@ -3282,7 +3218,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
}
- if((conn->httpversion == 20) && data->req.upload_chunky)
+ if((conn->httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@@ -3669,7 +3605,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
#endif
)) {
/* the ALPN of the current request */
- enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ enum alpnid id = (conn->httpversion == 30)? ALPN_h3 :
+ (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
result = Curl_altsvc_parse(data, data->asi,
headp + strlen("Alt-Svc:"),
id, conn->host.name,
@@ -3963,7 +3900,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* switch to http2 now. The bytes after response headers
are also processed here, otherwise they are lost. */
- result = Curl_http2_switched(data, k->str, *nread);
+ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
+ k->str, *nread);
if(result)
return result;
*nread = 0;
@@ -3971,7 +3909,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
#ifdef USE_WEBSOCKETS
else if(k->upgr101 == UPGR101_WS) {
/* verify the response */
- result = Curl_ws_accept(data);
+ result = Curl_ws_accept(data, k->str, *nread);
if(result)
return result;
k->header = FALSE; /* no more header to parse! */
@@ -4191,11 +4129,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
stream. In order to do this, we keep reading until we
close the stream. */
if(0 == k->maxdownload
-#if defined(USE_NGHTTP2)
- && !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- conn->httpversion == 20)
-#endif
- )
+ && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
+ && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
*stop_reading = TRUE;
if(*stop_reading) {
@@ -4290,7 +4225,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
if(conn->httpversion < 20) {
conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
- infof(data, "Mark bundle as not supporting multiuse");
}
}
else if(!nc) {
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index ecfe4eed0a..444abc0be6 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,6 +24,11 @@
*
***************************************************************************/
#include "curl_setup.h"
+
+#if defined(USE_MSH3) && !defined(_WIN32)
+#include <pthread.h>
+#endif
+
#include "ws.h"
typedef enum {
@@ -37,11 +42,7 @@ typedef enum {
#ifndef CURL_DISABLE_HTTP
-#ifdef USE_NGHTTP2
-#include <nghttp2/nghttp2.h>
-#endif
-
-#if defined(_WIN32) && defined(ENABLE_QUIC)
+#if defined(ENABLE_QUIC) || defined(USE_NGHTTP2)
#include <stdint.h>
#endif
@@ -73,8 +74,10 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
const struct connectdata *conn,
const char *thisheader,
const size_t thislen);
+struct HTTP; /* see below */
CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data,
+ struct HTTP *http,
curl_off_t *bytes_written,
curl_off_t included_body_bytes,
int socketindex);
@@ -179,29 +182,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
struct h3out; /* see ngtcp2 */
#endif
-#ifdef USE_MSH3
-#ifdef _WIN32
-#define msh3_lock CRITICAL_SECTION
-#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
-#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
-#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
-#define msh3_lock_release(lock) LeaveCriticalSection(lock)
-#else /* !_WIN32 */
-#include <pthread.h>
-#define msh3_lock pthread_mutex_t
-#define msh3_lock_initialize(lock) { \
- pthread_mutexattr_t attr; \
- pthread_mutexattr_init(&attr); \
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
- pthread_mutex_init(lock, &attr); \
- pthread_mutexattr_destroy(&attr); \
-}
-#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
-#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
-#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
-#endif /* _WIN32 */
-#endif /* USE_MSH3 */
-
/****************************************************************************
* HTTP unique setup
***************************************************************************/
@@ -220,6 +200,7 @@ struct HTTP {
void *fread_in; /* backup storage for fread_in pointer */
const char *postdata;
curl_off_t postsize;
+ struct Curl_easy *data;
} backup;
enum {
@@ -258,7 +239,6 @@ struct HTTP {
#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3)
bool bodystarted;
int status_code; /* HTTP status code */
- bool closed; /* TRUE on HTTP2 stream close */
char *mem; /* points to a buffer in memory to store received data */
size_t len; /* size of the buffer 'mem' points to */
size_t memlen; /* size of data copied to mem */
@@ -268,6 +248,8 @@ struct HTTP {
const uint8_t *upload_mem; /* points to a buffer to read from */
size_t upload_len; /* size of the buffer 'upload_mem' points to */
curl_off_t upload_left; /* number of bytes left to upload */
+ bool closed; /* TRUE on stream close */
+ bool reset; /* TRUE on stream reset */
#endif
#ifdef ENABLE_QUIC
@@ -278,20 +260,25 @@ struct HTTP {
bool firstheader; /* FALSE until headers arrive */
bool firstbody; /* FALSE until body arrives */
bool h3req; /* FALSE until request is issued */
-#endif
+#endif /* !USE_MSH3 */
bool upload_done;
-#endif
+#endif /* ENABLE_QUIC */
#ifdef USE_NGHTTP3
- size_t unacked_window;
+ size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
struct h3out *h3out; /* per-stream buffers for upload */
struct dynbuf overflow; /* excess data received during a single Curl_read */
-#endif
+#endif /* USE_NGHTTP3 */
#ifdef USE_MSH3
struct MSH3_REQUEST *req;
- msh3_lock recv_lock;
+#ifdef _WIN32
+ CRITICAL_SECTION recv_lock;
+#else /* !_WIN32 */
+ pthread_mutex_t recv_lock;
+#endif /* _WIN32 */
/* Receive Buffer (Headers and Data) */
uint8_t* recv_buf;
size_t recv_buf_alloc;
+ size_t recv_buf_max;
/* Receive Headers */
size_t recv_header_len;
bool recv_header_complete;
@@ -300,53 +287,13 @@ struct HTTP {
bool recv_data_complete;
/* General Receive Error */
CURLcode recv_error;
-#endif
-};
-
-#ifdef USE_NGHTTP2
-/* h2 settings for this connection */
-struct h2settings {
- uint32_t max_concurrent_streams;
- bool enable_push;
-};
-#endif
-
-struct http_conn {
-#ifdef USE_NGHTTP2
-#define H2_BINSETTINGS_LEN 80
- uint8_t binsettings[H2_BINSETTINGS_LEN];
- size_t binlen; /* length of the binsettings data */
-
- /* We associate the connectdata struct with the connection, but we need to
- make sure we can identify the current "driving" transfer. This is a
- work-around for the lack of nghttp2_session_set_user_data() in older
- nghttp2 versions that we want to support. (Added in 1.31.0) */
- struct Curl_easy *trnsfr;
-
- nghttp2_session *h2;
- Curl_send *send_underlying; /* underlying send Curl_send callback */
- Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */
- char *inbuf; /* buffer to receive data from underlying socket */
- size_t inbuflen; /* number of bytes filled in inbuf */
- size_t nread_inbuf; /* number of bytes read from in inbuf */
- /* We need separate buffer for transmission and reception because we
- may call nghttp2_session_send() after the
- nghttp2_session_mem_recv() but mem buffer is still not full. In
- this case, we wrongly sends the content of mem buffer if we share
- them for both cases. */
- int32_t pause_stream_id; /* stream ID which paused
- nghttp2_session_mem_recv */
- size_t drain_total; /* sum of all stream's UrlState.drain */
-
- /* this is a hash of all individual streams (Curl_easy structs) */
- struct h2settings settings;
-
- /* list of settings that will be sent */
- nghttp2_settings_entry local_settings[3];
- size_t local_settings_num;
-#else
- int unused; /* prevent a compiler warning */
-#endif
+#endif /* USE_MSH3 */
+#ifdef USE_QUICHE
+ bool h3_got_header; /* TRUE when h3 stream has recvd some HEADER */
+ bool h3_recving_data; /* TRUE when h3 stream is reading DATA */
+ bool h3_body_pending; /* TRUE when h3 stream may have more body DATA */
+ struct h3_event_node *pending;
+#endif /* USE_QUICHE */
};
CURLcode Curl_http_size(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index b9d3245cf8..bdb5e7378e 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -35,6 +35,7 @@
#include "strcase.h"
#include "multiif.h"
#include "url.h"
+#include "cfilters.h"
#include "connect.h"
#include "strtoofft.h"
#include "strdup.h"
@@ -63,124 +64,322 @@
#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
-#ifdef DEBUG_HTTP2
-#define H2BUGF(x) x
-#else
-#define H2BUGF(x) do { } while(0)
-#endif
-static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
- char *mem, size_t len, CURLcode *err);
-static bool http2_connisdead(struct Curl_easy *data,
- struct connectdata *conn);
-static int h2_session_send(struct Curl_easy *data,
- nghttp2_session *h2);
-static int h2_process_pending_input(struct Curl_easy *data,
- struct http_conn *httpc,
- CURLcode *err);
+#define H2_SETTINGS_IV_LEN 3
+#define H2_BINSETTINGS_LEN 80
-/*
- * Curl_http2_init_state() is called when the easy handle is created and
- * allows for HTTP/2 specific init of state.
- */
-void Curl_http2_init_state(struct UrlState *state)
+static int populate_settings(nghttp2_settings_entry *iv,
+ struct Curl_easy *data)
{
- state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
+ iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
+
+ iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+ iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
+
+ iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
+ iv[2].value = data->multi->push_cb != NULL;
+
+ return 3;
+}
+
+static size_t populate_binsettings(uint8_t *binsettings,
+ struct Curl_easy *data)
+{
+ nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
+ int ivlen;
+
+ ivlen = populate_settings(iv, data);
+ /* this returns number of bytes it wrote */
+ return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
+ iv, ivlen);
+}
+
+struct cf_h2_ctx {
+ nghttp2_session *h2;
+ uint32_t max_concurrent_streams;
+ bool enable_push;
+ /* The easy handle used in the current filter call, cleared at return */
+ struct cf_call_data call_data;
+
+ char *inbuf; /* buffer to receive data from underlying socket */
+ size_t inbuflen; /* number of bytes filled in inbuf */
+ size_t nread_inbuf; /* number of bytes read from in inbuf */
+
+ struct dynbuf outbuf;
+
+ /* We need separate buffer for transmission and reception because we
+ may call nghttp2_session_send() after the
+ nghttp2_session_mem_recv() but mem buffer is still not full. In
+ this case, we wrongly sends the content of mem buffer if we share
+ them for both cases. */
+ int32_t pause_stream_id; /* stream ID which paused
+ nghttp2_session_mem_recv */
+ size_t drain_total; /* sum of all stream's UrlState.drain */
+};
+
+/* How to access `call_data` from a cf_h2 filter */
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct cf_h2_ctx *)(cf)->ctx)->call_data
+
+
+static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
+{
+ struct cf_call_data save = ctx->call_data;
+
+ if(ctx->h2) {
+ nghttp2_session_del(ctx->h2);
+ }
+ free(ctx->inbuf);
+ Curl_dyn_free(&ctx->outbuf);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->call_data = save;
+}
+
+static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
+{
+ if(ctx) {
+ cf_h2_ctx_clear(ctx);
+ free(ctx);
+ }
+}
+
+static int h2_client_new(struct Curl_cfilter *cf,
+ nghttp2_session_callbacks *cbs)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+
+#if NGHTTP2_VERSION_NUM < 0x013200
+ /* before 1.50.0 */
+ return nghttp2_session_client_new(&ctx->h2, cbs, cf);
+#else
+ nghttp2_option *o;
+ int rc = nghttp2_option_new(&o);
+ if(rc)
+ return rc;
+ /* 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);
+ rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
+ nghttp2_option_del(o);
+ return rc;
+#endif
}
+static ssize_t send_callback(nghttp2_session *h2,
+ const uint8_t *mem, size_t length, int flags,
+ void *userp);
+static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
+ void *userp);
+static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
+ int32_t stream_id,
+ const uint8_t *mem, size_t len, void *userp);
+static int on_stream_close(nghttp2_session *session, int32_t stream_id,
+ uint32_t error_code, void *userp);
+static int on_begin_headers(nghttp2_session *session,
+ const nghttp2_frame *frame, void *userp);
+static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen,
+ uint8_t flags,
+ void *userp);
+static int error_callback(nghttp2_session *session, const char *msg,
+ size_t len, void *userp);
+
/*
- * Curl_http2_init_userset() is called when the easy handle is created and
- * allows for HTTP/2 specific user-set fields.
+ * multi_connchanged() is called to tell that there is a connection in
+ * this multi handle that has changed state (multiplexing become possible, the
+ * number of allowed streams changed or similar), and a subsequent use of this
+ * multi handle should move CONNECT_PEND handles back to CONNECT to have them
+ * retry.
*/
-void Curl_http2_init_userset(struct UserDefined *set)
+static void multi_connchanged(struct Curl_multi *multi)
{
- set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+ multi->recheckstate = TRUE;
}
-static int http2_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *sock)
+static CURLcode http2_data_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- const struct http_conn *c = &conn->proto.httpc;
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
struct HTTP *stream = data->req.p.http;
- sock[0] = conn->sock[FIRSTSOCKET];
+ (void)cf;
+ DEBUGASSERT(stream);
+ DEBUGASSERT(data->state.buffer);
- if(!(k->keepon & KEEP_RECV_PAUSE))
- /* Unless paused - in an HTTP/2 connection we can basically always get a
- frame so we should always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+ stream->stream_id = -1;
- /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
- there's a window to send data in */
- if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
- nghttp2_session_want_write(c->h2)) &&
- (nghttp2_session_get_remote_window_size(c->h2) &&
- nghttp2_session_get_stream_remote_window_size(c->h2,
- stream->stream_id)))
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+ Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
+ Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
- return bitmap;
+ stream->bodystarted = FALSE;
+ stream->status_code = -1;
+ stream->pausedata = NULL;
+ stream->pauselen = 0;
+ stream->closed = FALSE;
+ stream->close_handled = FALSE;
+ stream->memlen = 0;
+ stream->error = NGHTTP2_NO_ERROR;
+ stream->upload_left = 0;
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+ stream->mem = data->state.buffer;
+ stream->len = data->set.buffer_size;
+
+ return CURLE_OK;
}
/*
- * http2_stream_free() free HTTP2 stream related data
+ * Initialize the cfilter context
*/
-static void http2_stream_free(struct HTTP *http)
+static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool via_h1_upgrade)
{
- if(http) {
- Curl_dyn_free(&http->header_recvbuf);
- for(; http->push_headers_used > 0; --http->push_headers_used) {
- free(http->push_headers[http->push_headers_used - 1]);
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ int rc;
+ nghttp2_session_callbacks *cbs = NULL;
+
+ DEBUGASSERT(!ctx->h2);
+ ctx->inbuf = malloc(H2_BUFSIZE);
+ if(!ctx->inbuf)
+ goto out;
+ /* we want to aggregate small frames, SETTINGS, PRIO, UPDATES */
+ Curl_dyn_init(&ctx->outbuf, 4*1024);
+
+ rc = nghttp2_session_callbacks_new(&cbs);
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2 callbacks");
+ goto out;
+ }
+
+ nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
+ nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ cbs, on_data_chunk_recv);
+ nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ cbs, on_begin_headers);
+ nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
+ nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
+
+ /* The nghttp2 session is not yet setup, do it */
+ rc = h2_client_new(cf, cbs);
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2");
+ goto out;
+ }
+ ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
+
+ result = http2_data_setup(cf, data);
+ if(result)
+ goto out;
+
+ if(via_h1_upgrade) {
+ /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
+ * in the H1 request and we upgrade from there. This stream
+ * is opened implicitly as #1. */
+ uint8_t binsettings[H2_BINSETTINGS_LEN];
+ size_t binlen; /* length of the binsettings data */
+
+ binlen = populate_binsettings(binsettings, data);
+
+ stream->stream_id = 1;
+ /* queue SETTINGS frame (again) */
+ rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
+ data->state.httpreq == HTTPREQ_HEAD,
+ NULL);
+ if(rc) {
+ failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
+ }
+
+ rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->stream_id,
+ data);
+ if(rc) {
+ infof(data, "http/2: failed to set user_data for stream %u",
+ stream->stream_id);
+ DEBUGASSERT(0);
}
- free(http->push_headers);
- http->push_headers = NULL;
}
-}
+ else {
+ nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
+ int ivlen;
-/*
- * Disconnects *a* connection used for HTTP/2. It might be an old one from the
- * connection cache and not the "main" one. Don't touch the easy handle!
- */
+ /* H2 Settings need to be submitted. Stream is not open yet. */
+ DEBUGASSERT(stream->stream_id == -1);
-static CURLcode http2_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- struct http_conn *c = &conn->proto.httpc;
- (void)dead_connection;
-#ifndef DEBUG_HTTP2
- (void)data;
-#endif
+ ivlen = populate_settings(iv, data);
+ rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
+ iv, ivlen);
+ if(rc) {
+ failf(data, "nghttp2_submit_settings() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
+ }
+ }
- H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
+ rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
+ HTTP2_HUGE_WINDOW_SIZE);
+ if(rc) {
+ failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
+ }
- nghttp2_session_del(c->h2);
- Curl_safefree(c->inbuf);
+ /* all set, traffic will be send on connect */
+ result = CURLE_OK;
- H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
+out:
+ if(cbs)
+ nghttp2_session_callbacks_del(cbs);
+ return result;
+}
- return CURLE_OK;
+static CURLcode h2_session_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static int h2_process_pending_input(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err);
+
+/*
+ * http2_stream_free() free HTTP2 stream related data
+ */
+static void http2_stream_free(struct HTTP *stream)
+{
+ if(stream) {
+ Curl_dyn_free(&stream->header_recvbuf);
+ for(; stream->push_headers_used > 0; --stream->push_headers_used) {
+ free(stream->push_headers[stream->push_headers_used - 1]);
+ }
+ free(stream->push_headers);
+ stream->push_headers = NULL;
+ }
}
/*
* The server may send us data at any point (e.g. PING frames). Therefore,
* we cannot assume that an HTTP/2 socket is dead just because it is readable.
*
- * Instead, if it is readable, run Curl_connalive() to peek at the socket
+ * Check the lower filters first and, if successful, peek at the socket
* and distinguish between closed and data.
*/
-static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
+static bool http2_connisdead(struct Curl_cfilter *cf, struct Curl_easy *data)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
int sval;
bool dead = TRUE;
- if(conn->bits.close)
+ if(!cf->next || !cf->next->cft->is_alive(cf->next, data))
return TRUE;
- sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
+ sval = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
if(sval == 0) {
/* timeout */
dead = FALSE;
@@ -190,177 +389,57 @@ static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
dead = TRUE;
}
else if(sval & CURL_CSELECT_IN) {
- /* readable with no error. could still be closed */
- dead = !Curl_connalive(data, conn);
- if(!dead) {
- /* This happens before we've sent off a request and the connection is
- not in use by any other transfer, there shouldn't be any data here,
- only "protocol frames" */
- CURLcode result;
- struct http_conn *httpc = &conn->proto.httpc;
- ssize_t nread = -1;
- if(httpc->recv_underlying)
- /* if called "too early", this pointer isn't setup yet! */
- nread = ((Curl_recv *)httpc->recv_underlying)(
- data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
- if(nread != -1) {
- H2BUGF(infof(data,
- "%d bytes stray data read before trying h2 connection",
- (int)nread));
- httpc->nread_inbuf = 0;
- httpc->inbuflen = nread;
- if(h2_process_pending_input(data, httpc, &result) < 0)
- /* immediate error, considered dead */
- dead = TRUE;
- }
- else
- /* the read failed so let's say this is dead anyway */
+ /* This happens before we've sent off a request and the connection is
+ not in use by any other transfer, there shouldn't be any data here,
+ only "protocol frames" */
+ CURLcode result;
+ ssize_t nread = -1;
+
+ Curl_attach_connection(data, cf->conn);
+ nread = Curl_conn_cf_recv(cf->next, data,
+ ctx->inbuf, H2_BUFSIZE, &result);
+ dead = FALSE;
+ if(nread != -1) {
+ DEBUGF(LOG_CF(data, cf, "%d bytes stray data read before trying "
+ "h2 connection", (int)nread));
+ ctx->nread_inbuf = 0;
+ ctx->inbuflen = nread;
+ if(h2_process_pending_input(cf, data, &result) < 0)
+ /* immediate error, considered dead */
dead = TRUE;
}
+ else
+ /* the read failed so let's say this is dead anyway */
+ dead = TRUE;
+ Curl_detach_connection(data);
}
return dead;
}
-/*
- * Set the transfer that is currently using this HTTP/2 connection.
- */
-static void set_transfer(struct http_conn *c,
- struct Curl_easy *data)
-{
- c->trnsfr = data;
-}
-
-/*
- * Get the transfer that is currently using this HTTP/2 connection.
- */
-static struct Curl_easy *get_transfer(struct http_conn *c)
-{
- DEBUGASSERT(c && c->trnsfr);
- return c->trnsfr;
-}
-
-static unsigned int http2_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
+static CURLcode http2_send_ping(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- unsigned int ret_val = CONNRESULT_NONE;
- struct http_conn *c = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
int rc;
- bool send_frames = false;
- if(checks_to_perform & CONNCHECK_ISDEAD) {
- if(http2_connisdead(data, conn))
- ret_val |= CONNRESULT_DEAD;
+ rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL);
+ if(rc) {
+ failf(data, "nghttp2_submit_ping() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ return CURLE_HTTP2;
}
- if(checks_to_perform & CONNCHECK_KEEPALIVE) {
- struct curltime now = Curl_now();
- timediff_t elapsed = Curl_timediff(now, conn->keepalive);
-
- if(elapsed > data->set.upkeep_interval_ms) {
- /* Perform an HTTP/2 PING */
- rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
- if(!rc) {
- /* Successfully added a PING frame to the session. Need to flag this
- so the frame is sent. */
- send_frames = true;
- }
- else {
- failf(data, "nghttp2_submit_ping() failed: %s(%d)",
- nghttp2_strerror(rc), rc);
- }
-
- conn->keepalive = now;
- }
+ rc = nghttp2_session_send(ctx->h2);
+ if(rc) {
+ failf(data, "nghttp2_session_send() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ return CURLE_SEND_ERROR;
}
-
- if(send_frames) {
- set_transfer(c, data); /* set the transfer */
- rc = nghttp2_session_send(c->h2);
- if(rc)
- failf(data, "nghttp2_session_send() failed: %s(%d)",
- nghttp2_strerror(rc), rc);
- }
-
- return ret_val;
-}
-
-/* called from http_setup_conn */
-void Curl_http2_setup_req(struct Curl_easy *data)
-{
- struct HTTP *http = data->req.p.http;
- http->bodystarted = FALSE;
- http->status_code = -1;
- http->pausedata = NULL;
- http->pauselen = 0;
- http->closed = FALSE;
- http->close_handled = FALSE;
- http->mem = NULL;
- http->len = 0;
- http->memlen = 0;
- http->error = NGHTTP2_NO_ERROR;
-}
-
-/* called from http_setup_conn */
-void Curl_http2_setup_conn(struct connectdata *conn)
-{
- conn->proto.httpc.settings.max_concurrent_streams =
- DEFAULT_MAX_CONCURRENT_STREAMS;
+ return CURLE_OK;
}
/*
- * HTTP2 handler interface. This isn't added to the general list of protocols
- * but will be used at run-time when the protocol is dynamically switched from
- * HTTP to HTTP2.
- */
-static const struct Curl_handler Curl_handler_http2 = {
- "HTTP", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- http2_getsock, /* proto_getsock */
- http2_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- http2_getsock, /* perform_getsock */
- http2_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- http2_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTP, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_STREAM /* flags */
-};
-
-static const struct Curl_handler Curl_handler_http2_ssl = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- http2_getsock, /* proto_getsock */
- http2_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- http2_getsock, /* perform_getsock */
- http2_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- http2_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-/*
* Store nghttp2 version info in this buffer.
*/
void Curl_http2_ver(char *p, size_t len)
@@ -369,31 +448,75 @@ void Curl_http2_ver(char *p, size_t len)
(void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
}
+static CURLcode flush_output(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ size_t buflen = Curl_dyn_len(&ctx->outbuf);
+ ssize_t written;
+ CURLcode result;
+
+ if(!buflen)
+ return CURLE_OK;
+
+ DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen));
+ written = Curl_conn_cf_send(cf->next, data, Curl_dyn_ptr(&ctx->outbuf),
+ buflen, &result);
+ if(written < 0) {
+ return result;
+ }
+ if((size_t)written < buflen) {
+ Curl_dyn_tail(&ctx->outbuf, buflen - (size_t)written);
+ return CURLE_AGAIN;
+ }
+ else {
+ Curl_dyn_reset(&ctx->outbuf);
+ }
+ return CURLE_OK;
+}
+
/*
* The implementation of nghttp2_send_callback type. Here we write |data| with
* size |length| to the network and return the number of bytes actually
* written. See the documentation of nghttp2_send_callback for the details.
*/
static ssize_t send_callback(nghttp2_session *h2,
- const uint8_t *mem, size_t length, int flags,
+ const uint8_t *buf, size_t blen, int flags,
void *userp)
{
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *c = &conn->proto.httpc;
- struct Curl_easy *data = get_transfer(c);
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t written;
CURLcode result = CURLE_OK;
+ size_t buflen = Curl_dyn_len(&ctx->outbuf);
(void)h2;
(void)flags;
+ DEBUGASSERT(data);
- if(!c->send_underlying)
- /* called before setup properly! */
- return NGHTTP2_ERR_CALLBACK_FAILURE;
-
- written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
- mem, length, &result);
+ if(blen < 1024 && (buflen + blen + 1 < ctx->outbuf.toobig)) {
+ result = Curl_dyn_addn(&ctx->outbuf, buf, blen);
+ if(result) {
+ failf(data, "Failed to add data to output buffer");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ return blen;
+ }
+ if(buflen) {
+ /* not adding, flush buffer */
+ result = flush_output(cf, data);
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+ failf(data, "Failed sending HTTP2 data");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "h2 conn send %zu bytes", blen));
+ written = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
if(result == CURLE_AGAIN) {
return NGHTTP2_ERR_WOULDBLOCK;
}
@@ -467,26 +590,33 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
/*
* This specific transfer on this connection has been "drained".
*/
-static void drained_transfer(struct Curl_easy *data,
- struct http_conn *httpc)
+static void drained_transfer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- DEBUGASSERT(httpc->drain_total >= data->state.drain);
- httpc->drain_total -= data->state.drain;
- data->state.drain = 0;
+ if(data->state.drain) {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ DEBUGASSERT(ctx->drain_total > 0);
+ ctx->drain_total--;
+ data->state.drain = 0;
+ }
}
/*
* Mark this transfer to get "drained".
*/
-static void drain_this(struct Curl_easy *data,
- struct http_conn *httpc)
+static void drain_this(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- data->state.drain++;
- httpc->drain_total++;
- DEBUGASSERT(httpc->drain_total >= data->state.drain);
+ if(!data->state.drain) {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ data->state.drain = 1;
+ ctx->drain_total++;
+ DEBUGASSERT(ctx->drain_total > 0);
+ }
}
-static struct Curl_easy *duphandle(struct Curl_easy *data)
+static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct Curl_easy *second = curl_easy_duphandle(data);
if(second) {
@@ -497,9 +627,8 @@ static struct Curl_easy *duphandle(struct Curl_easy *data)
}
else {
second->req.p.http = http;
- Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
- Curl_http2_setup_req(second);
- second->state.stream_weight = data->state.stream_weight;
+ http2_data_setup(cf, second);
+ second->state.priority.weight = data->state.priority.weight;
}
}
return second;
@@ -559,22 +688,23 @@ static int set_transfer_url(struct Curl_easy *data,
return 0;
}
-static int push_promise(struct Curl_easy *data,
- struct connectdata *conn,
+static int push_promise(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
const nghttp2_push_promise *frame)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
int rv; /* one of the CURL_PUSH_* defines */
- H2BUGF(infof(data, "PUSH_PROMISE received, stream %u",
- frame->promised_stream_id));
+
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] PUSH_PROMISE received",
+ frame->promised_stream_id));
if(data->multi->push_cb) {
struct HTTP *stream;
struct HTTP *newstream;
struct curl_pushheaders heads;
CURLMcode rc;
- struct http_conn *httpc;
size_t i;
/* clone the parent */
- struct Curl_easy *newhandle = duphandle(data);
+ struct Curl_easy *newhandle = h2_duphandle(cf, data);
if(!newhandle) {
infof(data, "failed to duplicate handle");
rv = CURL_PUSH_DENY; /* FAIL HARD */
@@ -584,7 +714,7 @@ static int push_promise(struct Curl_easy *data,
heads.data = data;
heads.frame = frame;
/* ask the application */
- H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
+ DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application"));
stream = data->req.p.http;
if(!stream) {
@@ -630,7 +760,7 @@ static int push_promise(struct Curl_easy *data,
/* approved, add to the multi handle and immediately switch to PERFORM
state with the given connection !*/
- rc = Curl_multi_add_perform(data->multi, newhandle, conn);
+ rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
if(rc) {
infof(data, "failed to add handle to multi");
http2_stream_free(newhandle->req.p.http);
@@ -640,8 +770,7 @@ static int push_promise(struct Curl_easy *data,
goto fail;
}
- httpc = &conn->proto.httpc;
- rv = nghttp2_session_set_stream_user_data(httpc->h2,
+ rv = nghttp2_session_set_stream_user_data(ctx->h2,
frame->promised_stream_id,
newhandle);
if(rv) {
@@ -655,84 +784,82 @@ static int push_promise(struct Curl_easy *data,
Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
}
else {
- H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
+ DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it"));
rv = CURL_PUSH_DENY;
}
fail:
return rv;
}
-/*
- * multi_connchanged() is called to tell that there is a connection in
- * this multi handle that has changed state (multiplexing become possible, the
- * number of allowed streams changed or similar), and a subsequent use of this
- * multi handle should move CONNECT_PEND handles back to CONNECT to have them
- * retry.
- */
-static void multi_connchanged(struct Curl_multi *multi)
-{
- multi->recheckstate = TRUE;
-}
-
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
void *userp)
{
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s = NULL;
struct HTTP *stream = NULL;
- struct Curl_easy *data = get_transfer(httpc);
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
int rv;
size_t left, ncopy;
int32_t stream_id = frame->hd.stream_id;
CURLcode result;
+ DEBUGASSERT(data);
if(!stream_id) {
/* stream ID zero is for connection-oriented stuff */
- if(frame->hd.type == NGHTTP2_SETTINGS) {
- uint32_t max_conn = httpc->settings.max_concurrent_streams;
- H2BUGF(infof(data, "Got SETTINGS"));
- httpc->settings.max_concurrent_streams =
- nghttp2_session_get_remote_settings(
+ DEBUGASSERT(data);
+ switch(frame->hd.type) {
+ case NGHTTP2_SETTINGS: {
+ uint32_t max_conn = ctx->max_concurrent_streams;
+ DEBUGF(LOG_CF(data, cf, "recv frame SETTINGS"));
+ ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
- httpc->settings.enable_push =
- nghttp2_session_get_remote_settings(
+ ctx->enable_push = nghttp2_session_get_remote_settings(
session, NGHTTP2_SETTINGS_ENABLE_PUSH);
- H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
- httpc->settings.max_concurrent_streams));
- H2BUGF(infof(data, "ENABLE_PUSH == %s",
- httpc->settings.enable_push?"TRUE":"false"));
- if(max_conn != httpc->settings.max_concurrent_streams) {
+ DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d",
+ ctx->max_concurrent_streams));
+ DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s",
+ ctx->enable_push ? "TRUE" : "false"));
+ if(data && max_conn != ctx->max_concurrent_streams) {
/* only signal change if the value actually changed */
- infof(data,
- "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
- httpc->settings.max_concurrent_streams);
+ DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u",
+ ctx->max_concurrent_streams));
multi_connchanged(data->multi);
}
+ break;
+ }
+ case NGHTTP2_GOAWAY:
+ if(data) {
+ infof(data, "recveived GOAWAY, error=%d, last_stream=%u",
+ frame->goaway.error_code, frame->goaway.last_stream_id);
+ multi_connchanged(data->multi);
+ }
+ break;
+ case NGHTTP2_WINDOW_UPDATE:
+ DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE"));
+ break;
+ default:
+ DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type));
}
return 0;
}
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
if(!data_s) {
- H2BUGF(infof(data,
- "No Curl_easy associated with stream: %u",
- stream_id));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] No Curl_easy associated",
+ stream_id));
return 0;
}
stream = data_s->req.p.http;
if(!stream) {
- H2BUGF(infof(data_s, "No proto pointer for stream: %u",
- stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] No proto pointer", stream_id));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
- frame->hd.type, stream_id));
-
switch(frame->hd.type) {
case NGHTTP2_DATA:
/* If body started on this stream, then receiving DATA is illegal. */
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame DATA", stream_id));
if(!stream->bodystarted) {
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
@@ -741,8 +868,17 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
+ if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ /* Stream has ended. If there is pending data, ensure that read
+ will occur to consume it. */
+ if(!data->state.drain && stream->memlen) {
+ drain_this(cf, data_s);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ }
break;
case NGHTTP2_HEADERS:
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame HEADERS", stream_id));
if(stream->bodystarted) {
/* Only valid HEADERS after body started is trailer HEADERS. We
buffer them in on_header callback. */
@@ -776,19 +912,18 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
stream->nread_header_recvbuf += ncopy;
DEBUGASSERT(stream->mem);
- H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
- ncopy, stream_id, stream->mem));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu header bytes, at %p",
+ stream_id, ncopy, (void *)stream->mem));
stream->len -= ncopy;
stream->memlen += ncopy;
- drain_this(data_s, httpc);
- /* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
- Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ drain_this(cf, data_s);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
break;
case NGHTTP2_PUSH_PROMISE:
- rv = push_promise(data_s, conn, &frame->push_promise);
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id));
+ rv = push_promise(cf, data_s, &frame->push_promise);
if(rv) { /* deny! */
int h2;
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
@@ -798,14 +933,18 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
if(nghttp2_is_fatal(h2))
return NGHTTP2_ERR_CALLBACK_FAILURE;
else if(rv == CURL_PUSH_ERROROUT) {
- DEBUGF(infof(data_s, "Fail the parent stream (too)"));
+ DEBUGF(LOG_CF(data_s, cf, "Fail the parent stream (too)"));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
break;
+ case NGHTTP2_RST_STREAM:
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv RST", stream_id));
+ stream->reset = TRUE;
+ break;
default:
- H2BUGF(infof(data_s, "Got frame type %x for stream %u",
- frame->hd.type, stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame %x",
+ stream_id, frame->hd.type));
break;
}
return 0;
@@ -815,15 +954,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *mem, size_t len, void *userp)
{
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream;
struct Curl_easy *data_s;
size_t nread;
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
- (void)session;
(void)flags;
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
+ DEBUGASSERT(CF_DATA_CURRENT(cf));
/* get the stream from the hash based on Stream ID */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
@@ -831,8 +970,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
/* Receiving a Stream ID not in the hash should not happen - unless
we have aborted a transfer artificially and there were more data
in the pipeline. Silently ignore. */
- H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n",
- stream_id));
+ DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%u] Data for unknown",
+ stream_id));
return 0;
}
@@ -846,36 +985,38 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
stream->len -= nread;
stream->memlen += nread;
- drain_this(data_s, &conn->proto.httpc);
-
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s) {
+ drain_this(cf, data_s);
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ }
- H2BUGF(infof(data_s, "%zu data received for stream %u "
- "(%zu left in buffer %p, total %zu)",
- nread, stream_id,
- stream->len, stream->mem,
- stream->memlen));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu DATA recvd, "
+ "(buffer now holds %zu, %zu still free in %p)",
+ stream_id, nread,
+ stream->memlen, stream->len, (void *)stream->mem));
if(nread < len) {
stream->pausedata = mem + nread;
stream->pauselen = len - nread;
- H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
- ", stream %u",
- len - nread, stream_id));
- data_s->conn->proto.httpc.pause_stream_id = stream_id;
-
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu not recvd -> NGHTTP2_ERR_PAUSE",
+ stream_id, len - nread));
+ ctx->pause_stream_id = stream_id;
+ drain_this(cf, data_s);
return NGHTTP2_ERR_PAUSE;
}
+#if 0
/* pause execution of nghttp2 if we received data for another handle
in order to process them first. */
- if(get_transfer(httpc) != data_s) {
- data_s->conn->proto.httpc.pause_stream_id = stream_id;
-
+ if(CF_DATA_CURRENT(cf) != data_s) {
+ ctx->pause_stream_id = stream_id;
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] not call_data -> NGHTTP2_ERR_PAUSE",
+ stream_id));
+ drain_this(cf, data_s);
return NGHTTP2_ERR_PAUSE;
}
+#endif
return 0;
}
@@ -883,15 +1024,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
static int on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *userp)
{
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s;
struct HTTP *stream;
- struct connectdata *conn = (struct connectdata *)userp;
int rv;
(void)session;
(void)stream_id;
if(stream_id) {
- struct http_conn *httpc;
/* get the stream from the hash based on Stream ID, stream ID zero is for
connection-oriented stuff */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
@@ -900,16 +1041,17 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
decided to reject stream (e.g., PUSH_PROMISE). */
return 0;
}
- H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
- nghttp2_http2_strerror(error_code), error_code, stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] on_stream_close(), %s (err %d)",
+ stream_id, nghttp2_http2_strerror(error_code), error_code));
stream = data_s->req.p.http;
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
stream->closed = TRUE;
- httpc = &conn->proto.httpc;
- drain_this(data_s, httpc);
- Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ if(CF_DATA_CURRENT(cf) != data_s) {
+ drain_this(cf, data_s);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ }
stream->error = error_code;
/* remove the entry from the hash as the stream is now gone */
@@ -919,12 +1061,12 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
stream_id);
DEBUGASSERT(0);
}
- if(stream_id == httpc->pause_stream_id) {
- H2BUGF(infof(data_s, "Stopped the pause stream"));
- httpc->pause_stream_id = 0;
+ if(stream_id == ctx->pause_stream_id) {
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed the pause stream",
+ stream_id));
+ ctx->pause_stream_id = 0;
}
- H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
- stream->stream_id = 0; /* cleared */
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed, cleared", stream_id));
}
return 0;
}
@@ -932,16 +1074,17 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
static int on_begin_headers(nghttp2_session *session,
const nghttp2_frame *frame, void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct HTTP *stream;
struct Curl_easy *data_s = NULL;
- (void)userp;
+ (void)cf;
data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
if(!data_s) {
return 0;
}
- H2BUGF(infof(data_s, "on_begin_headers() was called"));
+ DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called"));
if(frame->hd.type != NGHTTP2_HEADERS) {
return 0;
@@ -989,11 +1132,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
uint8_t flags,
void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct HTTP *stream;
struct Curl_easy *data_s;
int32_t stream_id = frame->hd.stream_id;
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
CURLcode result;
(void)flags;
@@ -1020,13 +1162,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
/* pseudo headers are lower case */
int rc = 0;
- char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
+ char *check = aprintf("%s:%d", cf->conn->host.name,
+ cf->conn->remote_port);
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(!strcasecompare(check, (const char *)value) &&
- ((conn->remote_port != conn->given->defport) ||
- !strcasecompare(conn->host.name, (const char *)value))) {
+ ((cf->conn->remote_port != cf->conn->given->defport) ||
+ !strcasecompare(cf->conn->host.name, (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
@@ -1075,11 +1218,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(stream->bodystarted) {
/* This is a trailer */
- H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
- value));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] trailer: %.*s: %.*s",
+ stream->stream_id,
+ (int)namelen, name,
+ (int)valuelen, value));
result = Curl_dyn_addf(&stream->trailer_recvbuf,
- "%.*s: %.*s\r\n", namelen, name,
- valuelen, value);
+ "%.*s: %.*s\r\n", (int)namelen, name,
+ (int)valuelen, value);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1110,11 +1255,11 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
- H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
- stream->status_code, data_s));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] status: HTTP/2 %03d",
+ stream->stream_id, stream->status_code));
return 0;
}
@@ -1134,11 +1279,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
- H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
- value));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] header: %.*s: %.*s",
+ stream->stream_id,
+ (int)namelen, name,
+ (int)valuelen, value));
return 0; /* 0 is successful */
}
@@ -1150,12 +1297,13 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
nghttp2_data_source *source,
void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct Curl_easy *data_s;
struct HTTP *stream = NULL;
size_t nread;
(void)source;
- (void)userp;
+ (void)cf;
if(stream_id) {
/* get the stream from the hash based on Stream ID, stream ID zero is for
connection-oriented stuff */
@@ -1186,9 +1334,8 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
else if(nread == 0)
return NGHTTP2_ERR_DEFERRED;
- H2BUGF(infof(data_s, "data_source_read_callback: "
- "returns %zu bytes stream %u",
- nread, stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] data_source_read_callback: "
+ "returns %zu bytes", stream_id, nread));
return nread;
}
@@ -1207,149 +1354,58 @@ static int error_callback(nghttp2_session *session,
}
#endif
-static void populate_settings(struct Curl_easy *data,
- struct http_conn *httpc)
+static void http2_data_done(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool premature)
{
- nghttp2_settings_entry *iv = httpc->local_settings;
-
- iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
- iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
-
- iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
- iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
-
- iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
- iv[2].value = data->multi->push_cb != NULL;
-
- httpc->local_settings_num = 3;
-}
-
-void Curl_http2_done(struct Curl_easy *data, bool premature)
-{
- struct HTTP *http = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
/* there might be allocated resources done before this got the 'h2' pointer
setup */
- Curl_dyn_free(&http->header_recvbuf);
- Curl_dyn_free(&http->trailer_recvbuf);
- if(http->push_headers) {
+ Curl_dyn_free(&stream->header_recvbuf);
+ Curl_dyn_free(&stream->trailer_recvbuf);
+ if(stream->push_headers) {
/* if they weren't used and then freed before */
- for(; http->push_headers_used > 0; --http->push_headers_used) {
- free(http->push_headers[http->push_headers_used - 1]);
+ for(; stream->push_headers_used > 0; --stream->push_headers_used) {
+ free(stream->push_headers[stream->push_headers_used - 1]);
}
- free(http->push_headers);
- http->push_headers = NULL;
+ free(stream->push_headers);
+ stream->push_headers = NULL;
}
- if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
- !httpc->h2) /* not HTTP/2 ? */
+ if(!ctx || !ctx->h2)
return;
/* do this before the reset handling, as that might clear ->stream_id */
- if(http->stream_id == httpc->pause_stream_id) {
- H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id));
- httpc->pause_stream_id = 0;
+ if(stream->stream_id && stream->stream_id == ctx->pause_stream_id) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] DONE, the pause stream",
+ stream->stream_id));
+ ctx->pause_stream_id = 0;
}
- if(premature || (!http->closed && http->stream_id)) {
+
+ if(premature || (!stream->closed && stream->stream_id)) {
/* RST_STREAM */
- set_transfer(httpc, data); /* set the transfer */
- H2BUGF(infof(data, "RST stream %u", http->stream_id));
- if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
- http->stream_id, NGHTTP2_STREAM_CLOSED))
- (void)nghttp2_session_send(httpc->h2);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] RST", stream->stream_id));
+ if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->stream_id, NGHTTP2_STREAM_CLOSED))
+ (void)nghttp2_session_send(ctx->h2);
}
if(data->state.drain)
- drained_transfer(data, httpc);
+ drained_transfer(cf, data);
/* -1 means unassigned and 0 means cleared */
- if(http->stream_id > 0) {
- int rv = nghttp2_session_set_stream_user_data(httpc->h2,
- http->stream_id, 0);
+ if(nghttp2_session_get_stream_user_data(ctx->h2, stream->stream_id)) {
+ int rv = nghttp2_session_set_stream_user_data(ctx->h2,
+ stream->stream_id, 0);
if(rv) {
infof(data, "http/2: failed to clear user_data for stream %u",
- http->stream_id);
+ stream->stream_id);
DEBUGASSERT(0);
}
- set_transfer(httpc, NULL);
- http->stream_id = 0;
}
}
-static int client_new(struct connectdata *conn,
- nghttp2_session_callbacks *callbacks)
-{
-#if NGHTTP2_VERSION_NUM < 0x013200
- /* before 1.50.0 */
- return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
-#else
- nghttp2_option *o;
- int rc = nghttp2_option_new(&o);
- if(rc)
- return rc;
- /* 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);
- rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn,
- o);
- nghttp2_option_del(o);
- return rc;
-#endif
-}
-
-/*
- * Initialize nghttp2 for a Curl connection
- */
-static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
-{
- if(!conn->proto.httpc.h2) {
- int rc;
- nghttp2_session_callbacks *callbacks;
-
- conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
- if(!conn->proto.httpc.inbuf)
- return CURLE_OUT_OF_MEMORY;
-
- rc = nghttp2_session_callbacks_new(&callbacks);
-
- if(rc) {
- failf(data, "Couldn't initialize nghttp2 callbacks");
- return CURLE_OUT_OF_MEMORY; /* most likely at least */
- }
-
- /* nghttp2_send_callback */
- nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
- /* nghttp2_on_frame_recv_callback */
- nghttp2_session_callbacks_set_on_frame_recv_callback
- (callbacks, on_frame_recv);
- /* nghttp2_on_data_chunk_recv_callback */
- nghttp2_session_callbacks_set_on_data_chunk_recv_callback
- (callbacks, on_data_chunk_recv);
- /* nghttp2_on_stream_close_callback */
- nghttp2_session_callbacks_set_on_stream_close_callback
- (callbacks, on_stream_close);
- /* nghttp2_on_begin_headers_callback */
- nghttp2_session_callbacks_set_on_begin_headers_callback
- (callbacks, on_begin_headers);
- /* nghttp2_on_header_callback */
- nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
-
- nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
-
- /* The nghttp2 session is not yet setup, do it */
- rc = client_new(conn, callbacks);
-
- nghttp2_session_callbacks_del(callbacks);
-
- if(rc) {
- failf(data, "Couldn't initialize nghttp2");
- return CURLE_OUT_OF_MEMORY; /* most likely at least */
- }
- }
- return CURLE_OK;
-}
-
/*
* Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
*/
@@ -1357,26 +1413,18 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
struct Curl_easy *data)
{
CURLcode result;
- ssize_t binlen;
char *base64;
size_t blen;
- struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
- uint8_t *binsettings = conn->proto.httpc.binsettings;
- struct http_conn *httpc = &conn->proto.httpc;
-
- populate_settings(data, httpc);
+ uint8_t binsettings[H2_BINSETTINGS_LEN];
+ size_t binlen; /* length of the binsettings data */
- /* this returns number of bytes it wrote */
- binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
- httpc->local_settings,
- httpc->local_settings_num);
+ binlen = populate_binsettings(binsettings, data);
if(binlen <= 0) {
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
Curl_dyn_free(req);
return CURLE_FAILED_INIT;
}
- conn->proto.httpc.binlen = binlen;
result = Curl_base64url_encode((const char *)binsettings, binlen,
&base64, &blen);
@@ -1400,10 +1448,10 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
/*
* Returns nonzero if current HTTP/2 session should be closed.
*/
-static int should_close_session(struct http_conn *httpc)
+static int should_close_session(struct cf_h2_ctx *ctx)
{
- return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
- !nghttp2_session_want_write(httpc->h2);
+ return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) &&
+ !nghttp2_session_want_write(ctx->h2);
}
/*
@@ -1412,65 +1460,65 @@ static int should_close_session(struct http_conn *httpc)
* This function returns 0 if it succeeds, or -1 and error code will
* be assigned to *err.
*/
-static int h2_process_pending_input(struct Curl_easy *data,
- struct http_conn *httpc,
+static int h2_process_pending_input(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
CURLcode *err)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
ssize_t nread;
- char *inbuf;
ssize_t rv;
- nread = httpc->inbuflen - httpc->nread_inbuf;
- inbuf = httpc->inbuf + httpc->nread_inbuf;
+ nread = ctx->inbuflen - ctx->nread_inbuf;
+ if(nread) {
+ char *inbuf = ctx->inbuf + ctx->nread_inbuf;
- set_transfer(httpc, data); /* set the transfer */
- rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
- if(rv < 0) {
- failf(data,
- "h2_process_pending_input: nghttp2_session_mem_recv() returned "
- "%zd:%s", rv, nghttp2_strerror((int)rv));
- *err = CURLE_RECV_ERROR;
- return -1;
- }
+ rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)inbuf, nread);
+ if(rv < 0) {
+ failf(data,
+ "h2_process_pending_input: nghttp2_session_mem_recv() returned "
+ "%zd:%s", rv, nghttp2_strerror((int)rv));
+ *err = CURLE_RECV_ERROR;
+ return -1;
+ }
- if(nread == rv) {
- H2BUGF(infof(data,
- "h2_process_pending_input: All data in connection buffer "
- "processed"));
- httpc->inbuflen = 0;
- httpc->nread_inbuf = 0;
- }
- else {
- httpc->nread_inbuf += rv;
- H2BUGF(infof(data,
- "h2_process_pending_input: %zu bytes left in connection "
- "buffer",
- httpc->inbuflen - httpc->nread_inbuf));
+ if(nread == rv) {
+ DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed"));
+ ctx->inbuflen = 0;
+ ctx->nread_inbuf = 0;
+ }
+ else {
+ ctx->nread_inbuf += rv;
+ DEBUGF(LOG_CF(data, cf, "h2_process_pending_input: %zu bytes left "
+ "in connection buffer",
+ ctx->inbuflen - ctx->nread_inbuf));
+ }
}
- rv = h2_session_send(data, httpc->h2);
+ rv = h2_session_send(cf, data);
if(rv) {
*err = CURLE_SEND_ERROR;
return -1;
}
- if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
+ if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
/* No more requests are allowed in the current session, so
the connection may not be reused. This is set when a
GOAWAY frame has been received or when the limit of stream
identifiers has been reached. */
- connclose(data->conn, "http/2: No new requests allowed");
+ connclose(cf->conn, "http/2: No new requests allowed");
}
- if(should_close_session(httpc)) {
+ if(should_close_session(ctx)) {
struct HTTP *stream = data->req.p.http;
- H2BUGF(infof(data,
+ DEBUGF(LOG_CF(data, cf,
"h2_process_pending_input: nothing to do in this session"));
- if(stream->error)
+ if(stream->reset)
+ *err = CURLE_PARTIAL_FILE;
+ else if(stream->error)
*err = CURLE_HTTP2;
else {
/* not an error per se, but should still close the connection */
- connclose(data->conn, "GOAWAY received");
+ connclose(cf->conn, "GOAWAY received");
*err = CURLE_OK;
}
return -1;
@@ -1478,79 +1526,72 @@ static int h2_process_pending_input(struct Curl_easy *data,
return 0;
}
-/*
- * Called from transfer.c:done_sending when we stop uploading.
- */
-CURLcode Curl_http2_done_sending(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
+ struct HTTP *stream = data->req.p.http;
- if((conn->handler == &Curl_handler_http2_ssl) ||
- (conn->handler == &Curl_handler_http2)) {
- /* make sure this is only attempted for HTTP/2 transfers */
- struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &conn->proto.httpc;
- nghttp2_session *h2 = httpc->h2;
-
- if(stream->upload_left) {
- /* If the stream still thinks there's data left to upload. */
+ if(!ctx || !ctx->h2)
+ goto out;
- stream->upload_left = 0; /* DONE! */
+ if(stream->upload_left) {
+ /* If the stream still thinks there's data left to upload. */
+ stream->upload_left = 0; /* DONE! */
- /* resume sending here to trigger the callback to get called again so
- that it can signal EOF to nghttp2 */
- (void)nghttp2_session_resume_data(h2, stream->stream_id);
- (void)h2_process_pending_input(data, httpc, &result);
- }
+ /* resume sending here to trigger the callback to get called again so
+ that it can signal EOF to nghttp2 */
+ (void)nghttp2_session_resume_data(ctx->h2, stream->stream_id);
+ (void)h2_process_pending_input(cf, data, &result);
+ }
- /* If nghttp2 still has pending frames unsent */
- if(nghttp2_session_want_write(h2)) {
- struct SingleRequest *k = &data->req;
- int rv;
+ /* If nghttp2 still has pending frames unsent */
+ if(nghttp2_session_want_write(ctx->h2)) {
+ struct SingleRequest *k = &data->req;
+ int rv;
- H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
+ DEBUGF(LOG_CF(data, cf, "HTTP/2 still wants to send data"));
- /* and attempt to send the pending frames */
- rv = h2_session_send(data, h2);
- if(rv)
- result = CURLE_SEND_ERROR;
+ /* and attempt to send the pending frames */
+ rv = h2_session_send(cf, data);
+ if(rv)
+ result = CURLE_SEND_ERROR;
- if(nghttp2_session_want_write(h2)) {
- /* re-set KEEP_SEND to make sure we are called again */
- k->keepon |= KEEP_SEND;
- }
+ if(nghttp2_session_want_write(ctx->h2)) {
+ /* re-set KEEP_SEND to make sure we are called again */
+ k->keepon |= KEEP_SEND;
}
}
+
+out:
return result;
}
-static ssize_t http2_handle_stream_close(struct connectdata *conn,
+static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct HTTP *stream, CURLcode *err)
{
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
- if(httpc->pause_stream_id == stream->stream_id) {
- httpc->pause_stream_id = 0;
+ if(ctx->pause_stream_id == stream->stream_id) {
+ ctx->pause_stream_id = 0;
}
- drained_transfer(data, httpc);
+ drained_transfer(cf, data);
- if(httpc->pause_stream_id == 0) {
- if(h2_process_pending_input(data, httpc, err) != 0) {
+ if(ctx->pause_stream_id == 0) {
+ if(h2_process_pending_input(cf, data, err) != 0) {
return -1;
}
}
- DEBUGASSERT(data->state.drain == 0);
-
/* Reset to FALSE to prevent infinite loop in readwrite_data function. */
stream->closed = FALSE;
if(stream->error == NGHTTP2_REFUSED_STREAM) {
- H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
- stream->stream_id));
- connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new "
+ "connection", stream->stream_id));
+ connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
data->state.refused_stream = TRUE;
*err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
return -1;
@@ -1597,10 +1638,24 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
stream->close_handled = TRUE;
- H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
+ DEBUGF(LOG_CF(data, cf, "http2_recv returns 0, http2_handle_stream_close"));
return 0;
}
+static int sweight_wanted(const struct Curl_easy *data)
+{
+ /* 0 weight is not set by user and we take the nghttp2 default one */
+ return data->set.priority.weight?
+ data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
+}
+
+static int sweight_in_effect(const struct Curl_easy *data)
+{
+ /* 0 weight is not set by user and we take the nghttp2 default one */
+ return data->state.priority.weight?
+ data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
+}
+
/*
* h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
* and dependency to the peer. It also stores the updated values in the state
@@ -1610,14 +1665,14 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
static void h2_pri_spec(struct Curl_easy *data,
nghttp2_priority_spec *pri_spec)
{
- struct HTTP *depstream = (data->set.stream_depends_on?
- data->set.stream_depends_on->req.p.http:NULL);
+ struct Curl_data_priority *prio = &data->set.priority;
+ struct HTTP *depstream = (prio->parent?
+ prio->parent->req.p.http:NULL);
int32_t depstream_id = depstream? depstream->stream_id:0;
- nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
- data->set.stream_depends_e);
- data->state.stream_weight = data->set.stream_weight;
- data->state.stream_depends_e = data->set.stream_depends_e;
- data->state.stream_depends_on = data->set.stream_depends_on;
+ nghttp2_priority_spec_init(pri_spec, depstream_id,
+ sweight_wanted(data),
+ data->set.priority.exclusive);
+ data->state.priority = *prio;
}
/*
@@ -1625,53 +1680,61 @@ static void h2_pri_spec(struct Curl_easy *data,
* dependency settings and if so it submits a PRIORITY frame with the updated
* info.
*/
-static int h2_session_send(struct Curl_easy *data,
- nghttp2_session *h2)
+static CURLcode h2_session_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
- set_transfer(httpc, data);
- if((data->set.stream_weight != data->state.stream_weight) ||
- (data->set.stream_depends_e != data->state.stream_depends_e) ||
- (data->set.stream_depends_on != data->state.stream_depends_on) ) {
+ int rv = 0;
+
+ if((sweight_wanted(data) != sweight_in_effect(data)) ||
+ (data->set.priority.exclusive != data->state.priority.exclusive) ||
+ (data->set.priority.parent != data->state.priority.parent) ) {
/* send new weight and/or dependency */
nghttp2_priority_spec pri_spec;
- int rv;
h2_pri_spec(data, &pri_spec);
-
- H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
- stream->stream_id, data));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Queuing PRIORITY",
+ stream->stream_id));
DEBUGASSERT(stream->stream_id != -1);
- rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
- &pri_spec);
+ rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->stream_id, &pri_spec);
if(rv)
- return rv;
+ goto out;
}
- return nghttp2_session_send(h2);
+ rv = nghttp2_session_send(ctx->h2);
+out:
+ if(nghttp2_is_fatal(rv)) {
+ DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
+ nghttp2_strerror(rv), rv));
+ return CURLE_SEND_ERROR;
+ }
+ return flush_output(cf, data);
}
-static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
- char *mem, size_t len, CURLcode *err)
+static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
{
- ssize_t nread;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+ struct cf_call_data save;
- (void)sockindex; /* we always do HTTP2 on sockindex 0 */
+ CF_DATA_SAVE(save, cf, data);
- if(should_close_session(httpc)) {
- H2BUGF(infof(data,
- "http2_recv: nothing to do in this session"));
- if(conn->bits.close) {
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "http2_recv: nothing to do in this session"));
+ if(cf->conn->bits.close) {
/* already marked for closure, return OK and we're done */
+ drained_transfer(cf, data);
*err = CURLE_OK;
- return 0;
+ nread = 0;
+ goto out;
}
*err = CURLE_HTTP2;
- return -1;
+ nread = -1;
+ goto out;
}
/* Nullify here because we call nghttp2_session_send() and they
@@ -1690,74 +1753,67 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
size_t left =
Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
size_t ncopy = CURLMIN(len, left);
- memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
+ memcpy(buf, Curl_dyn_ptr(&stream->header_recvbuf) +
stream->nread_header_recvbuf, ncopy);
stream->nread_header_recvbuf += ncopy;
- H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
- (int)ncopy));
- return ncopy;
+ DEBUGF(LOG_CF(data, cf, "recv: Got %d bytes from header_recvbuf",
+ (int)ncopy));
+ nread = ncopy;
+ goto out;
}
- H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
- data, stream->stream_id,
- nghttp2_session_get_local_window_size(httpc->h2),
- nghttp2_session_get_stream_local_window_size(httpc->h2,
- stream->stream_id)
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: win %u/%u",
+ stream->stream_id,
+ nghttp2_session_get_local_window_size(ctx->h2),
+ nghttp2_session_get_stream_local_window_size(ctx->h2,
+ stream->stream_id)
));
- if((data->state.drain) && stream->memlen) {
- H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
- stream->memlen, stream->stream_id,
- stream->mem, mem));
- if(mem != stream->mem) {
+ if(stream->memlen) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: DRAIN %zu bytes (%p => %p)",
+ stream->stream_id, stream->memlen,
+ (void *)stream->mem, (void *)buf));
+ if(buf != stream->mem) {
/* if we didn't get the same buffer this time, we must move the data to
the beginning */
- memmove(mem, stream->mem, stream->memlen);
+ memmove(buf, stream->mem, stream->memlen);
stream->len = len - stream->memlen;
- stream->mem = mem;
+ stream->mem = buf;
}
- if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
+
+ if(ctx->pause_stream_id == stream->stream_id && !stream->pausedata) {
/* We have paused nghttp2, but we have no pause data (see
on_data_chunk_recv). */
- httpc->pause_stream_id = 0;
- if(h2_process_pending_input(data, httpc, err) != 0) {
- return -1;
+ ctx->pause_stream_id = 0;
+ if(h2_process_pending_input(cf, data, err) != 0) {
+ nread = -1;
+ goto out;
}
}
}
else if(stream->pausedata) {
- DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
+ DEBUGASSERT(ctx->pause_stream_id == stream->stream_id);
nread = CURLMIN(len, stream->pauselen);
- memcpy(mem, stream->pausedata, nread);
+ memcpy(buf, stream->pausedata, nread);
stream->pausedata += nread;
stream->pauselen -= nread;
+ drain_this(cf, data);
if(stream->pauselen == 0) {
- H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
- DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
- httpc->pause_stream_id = 0;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Unpaused", stream->stream_id));
+ DEBUGASSERT(ctx->pause_stream_id == stream->stream_id);
+ ctx->pause_stream_id = 0;
stream->pausedata = NULL;
stream->pauselen = 0;
-
- /* When NGHTTP2_ERR_PAUSE is returned from
- data_source_read_callback, we might not process DATA frame
- fully. Calling nghttp2_session_mem_recv() again will
- continue to process DATA frame, but if there is no incoming
- frames, then we have to call it again with 0-length data.
- Without this, on_stream_close callback will not be called,
- and stream could be hanged. */
- if(h2_process_pending_input(data, httpc, err) != 0) {
- return -1;
- }
}
- H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
- nread, stream->stream_id));
- return nread;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: returns unpaused %zd bytes",
+ stream->stream_id, nread));
+ goto out;
}
- else if(httpc->pause_stream_id) {
+ else if(ctx->pause_stream_id) {
/* If a stream paused nghttp2_session_mem_recv previously, and has
not processed all data, it still refers to the buffer in
nghttp2_session. If we call nghttp2_session_mem_recv(), we may
@@ -1766,36 +1822,56 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
socket is not read. But it seems that usually streams are
notified with its drain property, and socket is read again
quickly. */
- if(stream->closed)
+ if(stream->closed) {
/* closed overrides paused */
- return 0;
- H2BUGF(infof(data, "stream %u is paused, pause id: %u",
- stream->stream_id, httpc->pause_stream_id));
+ drained_transfer(cf, data);
+ nread = 0;
+ goto out;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is paused, pause h2sid: %u",
+ stream->stream_id, ctx->pause_stream_id));
*err = CURLE_AGAIN;
- return -1;
+ nread = -1;
+ goto out;
}
else {
- /* remember where to store incoming data for this stream and how big the
- buffer is */
- stream->mem = mem;
+ /* We have nothing buffered for `data` and no other stream paused
+ * the processing of incoming data, we can therefore read new data
+ * from the network.
+ * If DATA is coming for this stream, we want to store it ad the
+ * `buf` passed in right away - saving us a copy.
+ */
+ stream->mem = buf;
stream->len = len;
stream->memlen = 0;
- if(httpc->inbuflen == 0) {
- nread = ((Curl_recv *)httpc->recv_underlying)(
- data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
+ if(ctx->inbuflen > 0) {
+ DEBUGF(LOG_CF(data, cf, "Use data left in connection buffer, nread=%zd",
+ ctx->inbuflen - ctx->nread_inbuf));
+ if(h2_process_pending_input(cf, data, err))
+ return -1;
+ }
- if(nread == -1) {
+ while(stream->memlen == 0 /* have no data for this stream */
+ && !ctx->pause_stream_id /* we are not paused either */
+ && ctx->inbuflen == 0) { /* and out input buffer is empty */
+ /* Receive data from the "lower" filters */
+ nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err);
+ if(nread < 0) {
if(*err != CURLE_AGAIN)
failf(data, "Failed receiving HTTP2 data");
- else if(stream->closed)
+ else if(stream->closed) {
/* received when the stream was already closed! */
- return http2_handle_stream_close(conn, data, stream, err);
+ nread = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
+ }
- return -1;
+ /* nothing to read from the lower layers, clear drain */
+ drained_transfer(cf, data);
+ nread = -1;
+ goto out;
}
-
- if(nread == 0) {
+ else if(nread == 0) {
if(!stream->closed) {
/* This will happen when the server or proxy server is SIGKILLed
during data transfer. We should emit an error since our data
@@ -1803,107 +1879,133 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
failf(data, "HTTP/2 stream %u was not closed cleanly before"
" end of the underlying stream",
stream->stream_id);
- *err = CURLE_HTTP2_STREAM;
- return -1;
+ drained_transfer(cf, data);
+ *err = CURLE_PARTIAL_FILE;
+ nread = -1;
+ goto out;
}
- H2BUGF(infof(data, "end of stream"));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] end of stream",
+ stream->stream_id));
*err = CURLE_OK;
- return 0;
+ nread = 0;
+ goto out;
}
- H2BUGF(infof(data, "nread=%zd", nread));
-
- httpc->inbuflen = nread;
-
- DEBUGASSERT(httpc->nread_inbuf == 0);
- }
- else {
- nread = httpc->inbuflen - httpc->nread_inbuf;
- (void)nread; /* silence warning, used in debug */
- H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
- nread));
+ DEBUGF(LOG_CF(data, cf, "read %zd from connection", nread));
+ ctx->inbuflen = nread;
+ DEBUGASSERT(ctx->nread_inbuf == 0);
+ if(h2_process_pending_input(cf, data, err))
+ return -1;
}
- if(h2_process_pending_input(data, httpc, err))
- return -1;
}
+
if(stream->memlen) {
ssize_t retlen = stream->memlen;
- H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
- retlen, stream->stream_id));
+
+ /* TODO: all this buffer handling is very brittle */
+ stream->len += stream->memlen;
stream->memlen = 0;
- if(httpc->pause_stream_id == stream->stream_id) {
+ if(ctx->pause_stream_id == stream->stream_id) {
/* data for this stream is returned now, but this stream caused a pause
already so we need it called again asap */
- H2BUGF(infof(data, "Data returned for PAUSED stream %u",
- stream->stream_id));
- }
- else if(!stream->closed) {
- drained_transfer(data, httpc);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Data returned for PAUSED stream",
+ stream->stream_id));
+ drain_this(cf, data);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
- else
+ else if(stream->closed) {
+ if(stream->reset || stream->error) {
+ nread = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
+ }
/* this stream is closed, trigger a another read ASAP to detect that */
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is closed now, run again",
+ stream->stream_id));
+ drain_this(cf, data);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ else {
+ drained_transfer(cf, data);
+ }
- return retlen;
+ *err = CURLE_OK;
+ nread = retlen;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_h2_recv -> %zd",
+ stream->stream_id, nread));
+ goto out;
+ }
+
+ if(stream->closed) {
+ nread = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
+ }
+
+ if(!data->state.drain && Curl_conn_cf_data_pending(cf->next, data)) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] pending data, set drain",
+ stream->stream_id));
+ drain_this(cf, data);
}
- if(stream->closed)
- return http2_handle_stream_close(conn, data, stream, err);
*err = CURLE_AGAIN;
- H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
- stream->stream_id));
- return -1;
+ nread = -1;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv -> AGAIN",
+ stream->stream_id));
+out:
+ CF_DATA_RESTORE(cf, save);
+ return nread;
}
-static ssize_t http2_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
{
/*
* Currently, we send request in this function, but this function is also
* used to send request body. It would be nice to add dedicated function for
* request.
*/
+ struct cf_h2_ctx *ctx = cf->ctx;
int rv;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
struct HTTP *stream = data->req.p.http;
nghttp2_nv *nva = NULL;
size_t nheader;
nghttp2_data_provider data_prd;
int32_t stream_id;
- nghttp2_session *h2 = httpc->h2;
nghttp2_priority_spec pri_spec;
CURLcode result;
struct h2h3req *hreq;
+ struct cf_call_data save;
- (void)sockindex;
-
- H2BUGF(infof(data, "http2_send len=%zu", len));
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGF(LOG_CF(data, cf, "send len=%zu", len));
if(stream->stream_id != -1) {
if(stream->close_handled) {
infof(data, "stream %u closed", stream->stream_id);
*err = CURLE_HTTP2_STREAM;
- return -1;
+ len = -1;
+ goto out;
}
else if(stream->closed) {
- return http2_handle_stream_close(conn, data, stream, err);
+ len = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
}
/* If stream_id != -1, we have dispatched request HEADERS, and now
are going to send or sending request body in DATA frame */
- stream->upload_mem = mem;
+ stream->upload_mem = buf;
stream->upload_len = len;
- rv = nghttp2_session_resume_data(h2, stream->stream_id);
+ rv = nghttp2_session_resume_data(ctx->h2, stream->stream_id);
if(nghttp2_is_fatal(rv)) {
*err = CURLE_SEND_ERROR;
- return -1;
+ len = -1;
+ goto out;
}
- rv = h2_session_send(data, h2);
- if(nghttp2_is_fatal(rv)) {
- *err = CURLE_SEND_ERROR;
- return -1;
+ result = h2_session_send(cf, data);
+ if(result) {
+ *err = result;
+ len = -1;
+ goto out;
}
len -= stream->upload_len;
@@ -1912,10 +2014,11 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
stream->upload_mem = NULL;
stream->upload_len = 0;
- if(should_close_session(httpc)) {
- H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
*err = CURLE_HTTP2;
- return -1;
+ len = -1;
+ goto out;
}
if(stream->upload_left) {
@@ -1923,15 +2026,15 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
following API will make nghttp2_session_want_write() return
nonzero if remote window allows it, which then libcurl checks
socket is writable or not. See http2_perform_getsock(). */
- nghttp2_session_resume_data(h2, stream->stream_id);
+ nghttp2_session_resume_data(ctx->h2, stream->stream_id);
}
#ifdef DEBUG_HTTP2
if(!len) {
infof(data, "http2_send: easy %p (stream %u) win %u/%u",
data, stream->stream_id,
- nghttp2_session_get_remote_window_size(httpc->h2),
- nghttp2_session_get_stream_remote_window_size(httpc->h2,
+ nghttp2_session_get_remote_window_size(ctx->h2),
+ nghttp2_session_get_stream_remote_window_size(ctx->h2,
stream->stream_id)
);
@@ -1939,13 +2042,14 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
infof(data, "http2_send returns %zu for stream %u", len,
stream->stream_id);
#endif
- return len;
+ goto out;
}
- result = Curl_pseudo_headers(data, mem, len, &hreq);
+ result = Curl_pseudo_headers(data, buf, len, NULL, &hreq);
if(result) {
*err = result;
- return -1;
+ len = -1;
+ goto out;
}
nheader = hreq->entries;
@@ -1953,7 +2057,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
if(!nva) {
Curl_pseudo_free(hreq);
*err = CURLE_OUT_OF_MEMORY;
- return -1;
+ len = -1;
+ goto out;
}
else {
unsigned int i;
@@ -1969,8 +2074,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
h2_pri_spec(data, &pri_spec);
- H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
- nghttp2_session_check_request_allowed(h2), (void *)data));
+ DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)",
+ nghttp2_session_check_request_allowed(ctx->h2), (void *)data));
switch(data->state.httpreq) {
case HTTPREQ_POST:
@@ -1985,42 +2090,40 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
data_prd.read_callback = data_source_read_callback;
data_prd.source.ptr = NULL;
- stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
&data_prd, data);
break;
default:
- stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
NULL, data);
}
Curl_safefree(nva);
if(stream_id < 0) {
- H2BUGF(infof(data,
- "http2_send() nghttp2_submit_request error (%s)%u",
- nghttp2_strerror(stream_id), stream_id));
+ DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
+ nghttp2_strerror(stream_id), stream_id));
*err = CURLE_SEND_ERROR;
- return -1;
+ len = -1;
+ goto out;
}
infof(data, "Using Stream ID: %u (easy handle %p)",
stream_id, (void *)data);
stream->stream_id = stream_id;
- rv = h2_session_send(data, h2);
- if(rv) {
- H2BUGF(infof(data,
- "http2_send() nghttp2_session_send error (%s)%d",
- nghttp2_strerror(rv), rv));
-
- *err = CURLE_SEND_ERROR;
- return -1;
+ result = h2_session_send(cf, data);
+ if(result) {
+ *err = result;
+ len = -1;
+ goto out;
}
- if(should_close_session(httpc)) {
- H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
*err = CURLE_HTTP2;
- return -1;
+ len = -1;
+ goto out;
}
/* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
@@ -2030,169 +2133,126 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
results that no writable socket check is performed. To workaround this,
we issue nghttp2_session_resume_data() here to bring back DATA
transmission from deferred state. */
- nghttp2_session_resume_data(h2, stream->stream_id);
+ nghttp2_session_resume_data(ctx->h2, stream->stream_id);
+out:
+ CF_DATA_RESTORE(cf, save);
return len;
}
-CURLcode Curl_http2_setup(struct Curl_easy *data,
- struct connectdata *conn)
+static int cf_h2_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *sock)
{
- CURLcode result;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
struct HTTP *stream = data->req.p.http;
+ int bitmap = GETSOCK_BLANK;
+ struct cf_call_data save;
- DEBUGASSERT(data->state.buffer);
+ CF_DATA_SAVE(save, cf, data);
+ sock[0] = Curl_conn_cf_get_socket(cf, data);
- stream->stream_id = -1;
+ if(!(k->keepon & KEEP_RECV_PAUSE))
+ /* Unless paused - in an HTTP/2 connection we can basically always get a
+ frame so we should always be ready for one */
+ bitmap |= GETSOCK_READSOCK(0);
- Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
- Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
+ /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
+ there's a window to send data in */
+ if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
+ nghttp2_session_want_write(ctx->h2)) &&
+ (nghttp2_session_get_remote_window_size(ctx->h2) &&
+ nghttp2_session_get_stream_remote_window_size(ctx->h2,
+ stream->stream_id)))
+ bitmap |= GETSOCK_WRITESOCK(0);
- stream->upload_left = 0;
- stream->upload_mem = NULL;
- stream->upload_len = 0;
- stream->mem = data->state.buffer;
- stream->len = data->set.buffer_size;
+ CF_DATA_RESTORE(cf, save);
+ return bitmap;
+}
- multi_connchanged(data->multi);
- /* below this point only connection related inits are done, which only needs
- to be done once per connection */
- if((conn->handler == &Curl_handler_http2_ssl) ||
- (conn->handler == &Curl_handler_http2))
- return CURLE_OK; /* already done */
+static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
- if(conn->handler->flags & PROTOPT_SSL)
- conn->handler = &Curl_handler_http2_ssl;
- else
- conn->handler = &Curl_handler_http2;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
- result = http2_init(data, conn);
- if(result) {
- Curl_dyn_free(&stream->header_recvbuf);
- return result;
+ /* Connect the lower filters first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
}
- infof(data, "Using HTTP2, server supports multiplexing");
+ *done = FALSE;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 20;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ CF_DATA_SAVE(save, cf, data);
+ if(!ctx->h2) {
+ result = cf_h2_ctx_init(cf, data, FALSE);
+ if(result)
+ goto out;
+ }
- httpc->inbuflen = 0;
- httpc->nread_inbuf = 0;
+ if(-1 == h2_process_pending_input(cf, data, &result)) {
+ result = CURLE_HTTP2;
+ goto out;
+ }
- httpc->pause_stream_id = 0;
- httpc->drain_total = 0;
+ *done = TRUE;
+ cf->connected = TRUE;
+ result = CURLE_OK;
- return CURLE_OK;
+out:
+ CF_DATA_RESTORE(cf, save);
+ return result;
}
-CURLcode Curl_http2_switched(struct Curl_easy *data,
- const char *mem, size_t nread)
+static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- CURLcode result;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
- int rv;
- struct HTTP *stream = data->req.p.http;
-
- result = Curl_http2_setup(data, conn);
- if(result)
- return result;
-
- httpc->recv_underlying = conn->recv[FIRSTSOCKET];
- httpc->send_underlying = conn->send[FIRSTSOCKET];
- conn->recv[FIRSTSOCKET] = http2_recv;
- conn->send[FIRSTSOCKET] = http2_send;
-
- if(data->req.upgr101 == UPGR101_RECEIVED) {
- /* stream 1 is opened implicitly on upgrade */
- stream->stream_id = 1;
- /* queue SETTINGS frame (again) */
- rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
- data->state.httpreq == HTTPREQ_HEAD, NULL);
- if(rv) {
- failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
+ struct cf_h2_ctx *ctx = cf->ctx;
- rv = nghttp2_session_set_stream_user_data(httpc->h2,
- stream->stream_id,
- data);
- if(rv) {
- infof(data, "http/2: failed to set user_data for stream %u",
- stream->stream_id);
- DEBUGASSERT(0);
- }
- }
- else {
- populate_settings(data, httpc);
+ if(ctx) {
+ struct cf_call_data save;
- /* stream ID is unknown at this point */
- stream->stream_id = -1;
- rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
- httpc->local_settings,
- httpc->local_settings_num);
- if(rv) {
- failf(data, "nghttp2_submit_settings() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
+ CF_DATA_SAVE(save, cf, data);
+ cf_h2_ctx_clear(ctx);
+ CF_DATA_RESTORE(cf, save);
}
+}
- rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
- HTTP2_HUGE_WINDOW_SIZE);
- if(rv) {
- failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
+static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
- /* we are going to copy mem to httpc->inbuf. This is required since
- mem is part of buffer pointed by stream->mem, and callbacks
- called by nghttp2_session_mem_recv() will write stream specific
- data into stream->mem, overwriting data already there. */
- if(H2_BUFSIZE < nread) {
- failf(data, "connection buffer size is too small to store data following "
- "HTTP Upgrade response header: buflen=%d, datalen=%zu",
- H2_BUFSIZE, nread);
- return CURLE_HTTP2;
+ (void)data;
+ if(ctx) {
+ cf_h2_ctx_free(ctx);
+ cf->ctx = NULL;
}
-
- infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
- " after upgrade: len=%zu",
- nread);
-
- if(nread)
- memcpy(httpc->inbuf, mem, nread);
-
- httpc->inbuflen = nread;
-
- DEBUGASSERT(httpc->nread_inbuf == 0);
-
- if(-1 == h2_process_pending_input(data, httpc, &result))
- return CURLE_HTTP2;
-
- return CURLE_OK;
}
-CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
+static CURLcode http2_data_pause(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool pause)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
+
DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- /* if it isn't HTTP/2, we're done */
- if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
- !data->conn->proto.httpc.h2)
- return CURLE_OK;
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
- else {
+ if(ctx && ctx->h2) {
struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
- int rv = nghttp2_session_set_local_window_size(httpc->h2,
+ CURLcode result;
+
+ int rv = nghttp2_session_set_local_window_size(ctx->h2,
NGHTTP2_FLAG_NONE,
stream->stream_id,
window);
@@ -2203,9 +2263,9 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
}
/* make sure the window update gets sent */
- rv = h2_session_send(data, httpc->h2);
- if(rv)
- return CURLE_SEND_ERROR;
+ result = h2_session_send(cf, data);
+ if(result)
+ return result;
DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
window, stream->stream_id));
@@ -2214,7 +2274,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
{
/* read out the stream local window again */
uint32_t window2 =
- nghttp2_session_get_stream_local_window_size(httpc->h2,
+ nghttp2_session_get_stream_local_window_size(ctx->h2,
stream->stream_id);
DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
window2, stream->stream_id));
@@ -2225,86 +2285,323 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
return CURLE_OK;
}
-CURLcode Curl_http2_add_child(struct Curl_easy *parent,
- struct Curl_easy *child,
- bool exclusive)
+static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
{
- if(parent) {
- struct Curl_http2_dep **tail;
- struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
- if(!dep)
- return CURLE_OUT_OF_MEMORY;
- dep->data = child;
-
- if(parent->set.stream_dependents && exclusive) {
- struct Curl_http2_dep *node = parent->set.stream_dependents;
- while(node) {
- node->data->set.stream_depends_on = child;
- node = node->next;
- }
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
- tail = &child->set.stream_dependents;
- while(*tail)
- tail = &(*tail)->next;
+ (void)arg2;
- DEBUGASSERT(!*tail);
- *tail = parent->set.stream_dependents;
- parent->set.stream_dependents = 0;
- }
+ CF_DATA_SAVE(save, cf, data);
+ switch(event) {
+ case CF_CTRL_DATA_SETUP: {
+ result = http2_data_setup(cf, data);
+ break;
+ }
+ case CF_CTRL_DATA_PAUSE: {
+ result = http2_data_pause(cf, data, (arg1 != 0));
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ result = http2_data_done_send(cf, data);
+ break;
+ }
+ case CF_CTRL_DATA_DONE: {
+ http2_data_done(cf, data, arg1 != 0);
+ break;
+ }
+ default:
+ break;
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
- tail = &parent->set.stream_dependents;
- while(*tail) {
- (*tail)->data->set.stream_depends_e = FALSE;
- tail = &(*tail)->next;
- }
+static bool cf_h2_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ if(ctx && ctx->inbuflen > 0 && ctx->nread_inbuf > ctx->inbuflen)
+ return TRUE;
+ return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
+}
- DEBUGASSERT(!*tail);
- *tail = dep;
+static bool cf_h2_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ result = (ctx && ctx->h2 && !http2_connisdead(cf, data));
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ result = http2_send_ping(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static CURLcode cf_h2_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+ size_t effective_max;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT:
+ DEBUGASSERT(pres1);
+
+ CF_DATA_SAVE(save, cf, data);
+ if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
+ /* the limit is what we have in use right now */
+ effective_max = CONN_INUSE(cf->conn);
+ }
+ else {
+ effective_max = ctx->max_concurrent_streams;
+ }
+ *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
+ CF_DATA_RESTORE(cf, save);
+ return CURLE_OK;
+ default:
+ break;
}
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
- child->set.stream_depends_on = parent;
- child->set.stream_depends_e = exclusive;
- return CURLE_OK;
+struct Curl_cftype Curl_cft_nghttp2 = {
+ "HTTP/2",
+ CF_TYPE_MULTIPLEX,
+ CURL_LOG_DEFAULT,
+ cf_h2_destroy,
+ cf_h2_connect,
+ cf_h2_close,
+ Curl_cf_def_get_host,
+ cf_h2_get_select_socks,
+ cf_h2_data_pending,
+ cf_h2_send,
+ cf_h2_recv,
+ cf_h2_cntrl,
+ cf_h2_is_alive,
+ cf_h2_keep_alive,
+ cf_h2_query,
+};
+
+static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_h2_ctx *ctx;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(data->conn);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx)
+ goto out;
+
+ result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ result = CURLE_OK;
+
+out:
+ if(result)
+ cf_h2_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
}
-void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
+static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct Curl_http2_dep *last = 0;
- struct Curl_http2_dep *data = parent->set.stream_dependents;
- DEBUGASSERT(child->set.stream_depends_on == parent);
+ struct Curl_cfilter *cf_h2 = NULL;
+ struct cf_h2_ctx *ctx;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
- while(data && data->data != child) {
- last = data;
- data = data->next;
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx)
+ goto out;
+
+ result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_insert_after(cf, cf_h2);
+ result = CURLE_OK;
+
+out:
+ if(result)
+ cf_h2_ctx_free(ctx);
+ return result;
+}
+
+bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data)
+{
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_nghttp2)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
}
+ return FALSE;
+}
- DEBUGASSERT(data);
+bool Curl_conn_is_http2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
+}
- if(data) {
- if(last) {
- last->next = data->next;
- }
- else {
- parent->set.stream_dependents = data->next;
+bool Curl_http2_may_switch(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ (void)sockindex;
+ if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* We don't support HTTP/2 proxies yet. Also it's debatable
+ whether or not this setting should apply to HTTP/2 proxies. */
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
+ return FALSE;
}
- free(data);
+#endif
+ return TRUE;
}
+ return FALSE;
+}
+
+CURLcode Curl_http2_switch(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2")));
+
+ result = http2_cfilter_add(&cf, data, conn, sockindex);
+ if(result)
+ return result;
- child->set.stream_depends_on = 0;
- child->set.stream_depends_e = FALSE;
+ result = cf_h2_ctx_init(cf, data, FALSE);
+ if(result)
+ return result;
+
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf, data, FALSE, &done);
+ }
+ return CURLE_OK;
+}
+
+CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf_h2;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_cf_is_http2(cf, data));
+
+ result = http2_cfilter_insert_after(cf, data);
+ if(result)
+ return result;
+
+ cf_h2 = cf->next;
+ result = cf_h2_ctx_init(cf_h2, data, FALSE);
+ if(result)
+ return result;
+
+ cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf_h2->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
+ }
+ return CURLE_OK;
}
-void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
+CURLcode Curl_http2_upgrade(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex,
+ const char *mem, size_t nread)
{
- while(data->set.stream_dependents) {
- struct Curl_easy *tmp = data->set.stream_dependents->data;
- Curl_http2_remove_child(data, tmp);
- if(data->set.stream_depends_on)
- Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
+ struct Curl_cfilter *cf;
+ struct cf_h2_ctx *ctx;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2")));
+ DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
+
+ result = http2_cfilter_add(&cf, data, conn, sockindex);
+ if(result)
+ return result;
+
+ DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
+ ctx = cf->ctx;
+
+ result = cf_h2_ctx_init(cf, data, TRUE);
+ if(result)
+ return result;
+
+ if(nread) {
+ /* we are going to copy mem to httpc->inbuf. This is required since
+ mem is part of buffer pointed by stream->mem, and callbacks
+ called by nghttp2_session_mem_recv() will write stream specific
+ data into stream->mem, overwriting data already there. */
+ if(H2_BUFSIZE < nread) {
+ failf(data, "connection buffer size is too small to store data "
+ "following HTTP Upgrade response header: buflen=%d, datalen=%zu",
+ H2_BUFSIZE, nread);
+ return CURLE_HTTP2;
+ }
+
+ infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
+ " after upgrade: len=%zu", nread);
+ DEBUGASSERT(ctx->nread_inbuf == 0);
+ memcpy(ctx->inbuf, mem, nread);
+ ctx->inbuflen = nread;
}
- if(data->set.stream_depends_on)
- Curl_http2_remove_child(data->set.stream_depends_on, data);
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf, data, FALSE, &done);
+ }
+ return CURLE_OK;
}
/* Only call this function for a transfer that already got an HTTP/2
@@ -2312,7 +2609,7 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
bool Curl_h2_http_1_1_error(struct Curl_easy *data)
{
struct HTTP *stream = data->req.p.http;
- return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
+ return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
}
#else /* !USE_NGHTTP2 */
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h
index 966bf75da0..f78fbf04e0 100644
--- a/Utilities/cmcurl/lib/http2.h
+++ b/Utilities/cmcurl/lib/http2.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -40,44 +40,41 @@ void Curl_http2_ver(char *p, size_t len);
const char *Curl_http2_strerror(uint32_t err);
-CURLcode Curl_http2_init(struct connectdata *conn);
-void Curl_http2_init_state(struct UrlState *state);
-void Curl_http2_init_userset(struct UserDefined *set);
CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
struct Curl_easy *data);
-CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn);
-CURLcode Curl_http2_switched(struct Curl_easy *data,
- const char *ptr, size_t nread);
-/* called from http_setup_conn */
-void Curl_http2_setup_conn(struct connectdata *conn);
-void Curl_http2_setup_req(struct Curl_easy *data);
-void Curl_http2_done(struct Curl_easy *data, bool premature);
-CURLcode Curl_http2_done_sending(struct Curl_easy *data,
- struct connectdata *conn);
-CURLcode Curl_http2_add_child(struct Curl_easy *parent,
- struct Curl_easy *child,
- bool exclusive);
-void Curl_http2_remove_child(struct Curl_easy *parent,
- struct Curl_easy *child);
-void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
-CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause);
/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
bool Curl_h2_http_1_1_error(struct Curl_easy *data);
+
+bool Curl_conn_is_http2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data);
+
+bool Curl_http2_may_switch(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_http2_switch(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex);
+
+CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data);
+
+CURLcode Curl_http2_upgrade(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex,
+ const char *ptr, size_t nread);
+
+extern struct Curl_cftype Curl_cft_nghttp2;
+
#else /* USE_NGHTTP2 */
+
+#define Curl_cf_is_http2(a,b) FALSE
+#define Curl_conn_is_http2(a,b,c) FALSE
+#define Curl_http2_may_switch(a,b,c) FALSE
+
#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_setup_conn(x) Curl_nop_stmt
-#define Curl_http2_setup_req(x)
-#define Curl_http2_init_state(x)
-#define Curl_http2_init_userset(x)
-#define Curl_http2_done(x,y)
-#define Curl_http2_done_sending(x,y) (void)y
-#define Curl_http2_add_child(x, y, z)
-#define Curl_http2_remove_child(x, y)
-#define Curl_http2_cleanup_dependencies(x)
-#define Curl_http2_stream_pause(x, y)
+#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_h2_http_1_1_error(x) 0
#endif
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 8c6d1c9dc8..f8ce1695e8 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,9 +48,9 @@
do { \
ret = Curl_hmacit(Curl_HMAC_SHA256, \
(unsigned char *)k, \
- (unsigned int)kl, \
+ kl, \
(unsigned char *)d, \
- (unsigned int)dl, o); \
+ dl, o); \
if(ret) { \
goto fail; \
} \
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h
index 85755e937b..57cc5706eb 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.h
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c
index c344e6d39a..bda00d3833 100644
--- a/Utilities/cmcurl/lib/http_chunks.c
+++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h
index 2cf5507c21..ed50713162 100644
--- a/Utilities/cmcurl/lib/http_chunks.h
+++ b/Utilities/cmcurl/lib/http_chunks.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index f015f78d92..8daad99e32 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h
index eea90b7439..7d5cfc1bfd 100644
--- a/Utilities/cmcurl/lib/http_digest.h
+++ b/Utilities/cmcurl/lib/http_digest.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c
index 5909f85b0d..153e3d4ab8 100644
--- a/Utilities/cmcurl/lib/http_negotiate.c
+++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_negotiate.h b/Utilities/cmcurl/lib/http_negotiate.h
index 6e2096c697..76d8356132 100644
--- a/Utilities/cmcurl/lib/http_negotiate.h
+++ b/Utilities/cmcurl/lib/http_negotiate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c
index a19cc0d589..b845ddf37f 100644
--- a/Utilities/cmcurl/lib/http_ntlm.c
+++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h
index cec63b82c3..f37572baec 100644
--- a/Utilities/cmcurl/lib/http_ntlm.h
+++ b/Utilities/cmcurl/lib/http_ntlm.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index e30730acab..fdd092d034 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,7 +26,7 @@
#include "http_proxy.h"
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+#if !defined(CURL_DISABLE_PROXY)
#include <curl/curl.h>
#ifdef USE_HYPER
@@ -49,6 +49,9 @@
#include "curl_memory.h"
#include "memdebug.h"
+
+#if !defined(CURL_DISABLE_HTTP)
+
typedef enum {
TUNNEL_INIT, /* init/default/no tunnel state */
TUNNEL_CONNECT, /* CONNECT request is being send */
@@ -63,8 +66,7 @@ struct tunnel_state {
int sockindex;
const char *hostname;
int remote_port;
- struct HTTP http_proxy;
- struct HTTP *prot_save;
+ struct HTTP CONNECT;
struct dynbuf rcvbuf;
struct dynbuf req;
size_t nsend;
@@ -149,17 +151,6 @@ static CURLcode tunnel_init(struct tunnel_state **pts,
Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
- /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
- * member conn->proto.http; we want [protocol] through HTTP and we have
- * to change the member temporarily for connecting to the HTTP
- * proxy. After Curl_proxyCONNECT we have to set back the member to the
- * original pointer
- *
- * This function might be called several times in the multi interface case
- * if the proxy's CONNECT response is not instant.
- */
- ts->prot_save = data->req.p.http;
- data->req.p.http = &ts->http_proxy;
*pts = ts;
connkeep(conn, "HTTP proxy CONNECT");
return tunnel_reinit(ts, conn, data);
@@ -183,34 +174,39 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
/* entering this one */
switch(new_state) {
case TUNNEL_INIT:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
tunnel_reinit(ts, cf->conn, data);
break;
case TUNNEL_CONNECT:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
ts->tunnel_state = TUNNEL_CONNECT;
ts->keepon = KEEPON_CONNECT;
Curl_dyn_reset(&ts->rcvbuf);
break;
case TUNNEL_RECEIVE:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
ts->tunnel_state = TUNNEL_RECEIVE;
break;
case TUNNEL_RESPONSE:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
ts->tunnel_state = TUNNEL_RESPONSE;
break;
case TUNNEL_ESTABLISHED:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
/* FALLTHROUGH */
case TUNNEL_FAILED:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
ts->tunnel_state = new_state;
Curl_dyn_reset(&ts->rcvbuf);
Curl_dyn_reset(&ts->req);
/* restore the protocol pointer */
- data->req.p.http = ts->prot_save;
data->info.httpcode = 0; /* clear it as it might've been used for the
proxy */
/* If a proxy-authorization header was used for the proxy, then we should
@@ -271,10 +267,11 @@ static CURLcode CONNECT_host(struct Curl_easy *data,
}
#ifndef USE_HYPER
-static CURLcode start_CONNECT(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode start_CONNECT(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts)
{
+ struct connectdata *conn = cf->conn;
char *hostheader = NULL;
char *host = NULL;
const char *httpv;
@@ -338,7 +335,8 @@ static CURLcode start_CONNECT(struct Curl_easy *data,
goto out;
/* Send the connect request to the proxy */
- result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(&ts->req, data, &ts->CONNECT,
+ &data->info.request_size, 0,
ts->sockindex);
ts->headerlines = 0;
@@ -356,7 +354,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data,
bool *done)
{
struct SingleRequest *k = &data->req;
- struct HTTP *http = data->req.p.http;
+ struct HTTP *http = &ts->CONNECT;
CURLcode result = CURLE_OK;
if(http->sending != HTTPSEND_REQUEST)
@@ -377,7 +375,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data,
result = Curl_write(data,
conn->writesockfd, /* socket to send to */
k->upload_fromhere, /* buffer pointer */
- ts->nsend, /* buffer size */
+ ts->nsend, /* buffer size */
&bytes_written); /* actually sent */
if(result)
goto out;
@@ -398,13 +396,15 @@ out:
return result;
}
-static CURLcode on_resp_header(struct Curl_easy *data,
+static CURLcode on_resp_header(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
const char *header)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
int subversion = 0;
+ (void)cf;
if((checkprefix("WWW-Authenticate:", header) &&
(401 == k->httpcode)) ||
@@ -416,8 +416,7 @@ static CURLcode on_resp_header(struct Curl_easy *data,
if(!auth)
return CURLE_OUT_OF_MEMORY;
- DEBUGF(infof(data, "CONNECT: fwd auth header '%s'",
- header));
+ DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header));
result = Curl_http_input_auth(data, proxy, auth);
free(auth);
@@ -471,14 +470,14 @@ static CURLcode on_resp_header(struct Curl_easy *data,
return result;
}
-static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
bool *done)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
char *linep;
size_t perline;
int error;
@@ -634,7 +633,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
/* without content-length or chunked encoding, we
can't keep the connection alive since the close is
the end signal so we bail out at once instead */
- DEBUGF(infof(data, "CONNECT: no content-length or chunked"));
+ DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked"));
ts->keepon = KEEPON_DONE;
}
}
@@ -647,7 +646,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
continue;
}
- result = on_resp_header(data, ts, linep);
+ result = on_resp_header(cf, data, ts, linep);
if(result)
return result;
@@ -667,12 +666,13 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
#else /* USE_HYPER */
/* The Hyper version of CONNECT */
-static CURLcode start_CONNECT(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode start_CONNECT(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts)
{
+ struct connectdata *conn = cf->conn;
struct hyptransfer *h = &data->hyp;
- curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
hyper_io *io = NULL;
hyper_request *req = NULL;
hyper_headers *headers = NULL;
@@ -914,8 +914,8 @@ error:
return result;
}
-static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
bool *done)
{
@@ -925,7 +925,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
(void)ts;
*done = FALSE;
- result = Curl_hyper_stream(data, conn, &didwhat, done,
+ result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
CURL_CSELECT_IN | CURL_CSELECT_OUT);
if(result || !*done)
return result;
@@ -972,7 +972,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
switch(ts->tunnel_state) {
case TUNNEL_INIT:
/* Prepare the CONNECT request and make a first attempt to send. */
- result = start_CONNECT(data, cf->conn, ts);
+ DEBUGF(LOG_CF(data, cf, "CONNECT start"));
+ result = start_CONNECT(cf, data, ts);
if(result)
goto out;
tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
@@ -980,6 +981,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_CONNECT:
/* see that the request is completely sent */
+ DEBUGF(LOG_CF(data, cf, "CONNECT send"));
result = send_CONNECT(data, cf->conn, ts, &done);
if(result || !done)
goto out;
@@ -988,7 +990,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_RECEIVE:
/* read what is there */
- result = recv_CONNECT_resp(data, cf->conn, ts, &done);
+ DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
+ result = recv_CONNECT_resp(cf, data, ts, &done);
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
goto out;
@@ -1001,24 +1004,29 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case TUNNEL_RESPONSE:
+ DEBUGF(LOG_CF(data, cf, "CONNECT response"));
if(data->req.newurl) {
/* not the "final" response, we need to do a follow up request.
* If the other side indicated a connection close, or if someone
- * else told us to close this connection, do so now. */
+ * else told us to close this connection, do so now.
+ */
if(ts->close_connection || conn->bits.close) {
- /* Close the filter chain and trigger connect, non-blocking
- * again, so the process is ongoing. This will
- * a) the close resets our tunnel state
- * b) the connect makes sure that there will be a socket
- * to select on again.
- * We return and expect to be called again. */
+ /* Close this filter and the sub-chain, re-connect the
+ * sub-chain and continue. Closing this filter will
+ * reset our tunnel state. To avoid recursion, we return
+ * and expect to be called again.
+ */
+ DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open"));
infof(data, "Connect me again please");
- Curl_conn_close(data, cf->sockindex);
- result = cf->next->cft->connect(cf->next, data, FALSE, &done);
+ Curl_conn_cf_close(cf, data);
+ connkeep(conn, "HTTP proxy CONNECT");
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
goto out;
}
- /* staying on this connection, reset state */
- tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+ else {
+ /* staying on this connection, reset state */
+ tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+ }
}
break;
@@ -1063,10 +1071,12 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
+ DEBUGF(LOG_CF(data, cf, "connect"));
result = cf->next->cft->connect(cf->next, data, blocking, done);
if(result || !*done)
return result;
+ DEBUGF(LOG_CF(data, cf, "subchain is connected"));
/* TODO: can we do blocking? */
/* We want "seamless" operations through HTTP proxy tunnel */
@@ -1117,22 +1127,21 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
curl_socket_t *socks)
{
struct tunnel_state *ts = cf->ctx;
- struct connectdata *conn = cf->conn;
int fds;
- DEBUGASSERT(conn);
fds = cf->next->cft->get_select_socks(cf->next, data, socks);
if(!fds && cf->next->connected && !cf->connected) {
/* If we are not connected, but the filter "below" is
* and not waiting on something, we are tunneling. */
- socks[0] = conn->sock[cf->sockindex];
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
if(ts) {
/* when we've sent a CONNECT to a proxy, we should rather either
wait for the socket to become readable to be able to get the
response headers or if we're still sending the request, wait
for write. */
- if(ts->http_proxy.sending == HTTPSEND_REQUEST)
+ if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
return GETSOCK_WRITESOCK(0);
+ }
return GETSOCK_READSOCK(0);
}
return GETSOCK_WRITESOCK(0);
@@ -1140,24 +1149,18 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
return fds;
}
-static void http_proxy_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- if(cf->ctx) {
- tunnel_free(cf, data);
- }
-}
-
static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- http_proxy_cf_detach_data(cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ tunnel_free(cf, data);
}
static void http_proxy_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
+ DEBUGF(LOG_CF(data, cf, "close"));
cf->connected = FALSE;
cf->next->cft->close(cf->next, data);
if(cf->ctx) {
@@ -1166,11 +1169,11 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf,
}
-static const struct Curl_cftype cft_http_proxy = {
+struct Curl_cftype Curl_cft_http_proxy = {
"HTTP-PROXY",
CF_TYPE_IP_CONNECT,
+ 0,
http_proxy_cf_destroy,
- Curl_cf_def_setup,
http_proxy_cf_connect,
http_proxy_cf_close,
http_proxy_cf_get_host,
@@ -1178,8 +1181,10 @@ static const struct Curl_cftype cft_http_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- http_proxy_cf_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
@@ -1189,31 +1194,73 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_http_proxy, NULL);
+ result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
+CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ (void)data;
+ result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
+#endif /* ! CURL_DISABLE_HTTP */
-static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
- struct Curl_easy *data)
+
+typedef enum {
+ HAPROXY_INIT, /* init/default/no tunnel state */
+ HAPROXY_SEND, /* data_out being sent */
+ HAPROXY_DONE /* all work done */
+} haproxy_state;
+
+struct cf_haproxy_ctx {
+ int state;
+ struct dynbuf data_out;
+};
+
+static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
- struct dynbuf req;
+ DEBUGASSERT(ctx);
+ ctx->state = HAPROXY_INIT;
+ Curl_dyn_reset(&ctx->data_out);
+}
+
+static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
+{
+ if(ctx) {
+ Curl_dyn_free(&ctx->data_out);
+ free(ctx);
+ }
+}
+
+static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
+ struct Curl_easy *data)
+{
+ struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *tcp_version;
- Curl_dyn_init(&req, DYN_HAXPROXY);
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(ctx->state == HAPROXY_INIT);
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
/* the buffer is large enough to hold this! */
- result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n"));
+ result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
/* Emit the correct prefix for IPv6 */
tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
- result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
+ result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
tcp_version,
data->info.conn_local_ip,
data->info.conn_primary_ip,
@@ -1223,19 +1270,18 @@ static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
#ifdef USE_UNIX_SOCKETS
}
#endif /* USE_UNIX_SOCKETS */
-
- if(!result)
- result = Curl_buffer_send(&req, data, &data->info.request_size,
- 0, FIRSTSOCKET);
return result;
}
-static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
+static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
+ struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
+ size_t len;
+ DEBUGASSERT(ctx);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@@ -1245,28 +1291,120 @@ static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
if(result || !*done)
return result;
- result = send_haproxy_header(cf, data);
- *done = (!result);
+ switch(ctx->state) {
+ case HAPROXY_INIT:
+ result = cf_haproxy_date_out_set(cf, data);
+ if(result)
+ goto out;
+ ctx->state = HAPROXY_SEND;
+ /* FALLTHROUGH */
+ case HAPROXY_SEND:
+ len = Curl_dyn_len(&ctx->data_out);
+ if(len > 0) {
+ ssize_t written = Curl_conn_send(data, cf->sockindex,
+ Curl_dyn_ptr(&ctx->data_out),
+ len, &result);
+ if(written < 0)
+ goto out;
+ Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
+ if(Curl_dyn_len(&ctx->data_out) > 0) {
+ result = CURLE_OK;
+ goto out;
+ }
+ }
+ ctx->state = HAPROXY_DONE;
+ /* FALLTHROUGH */
+ default:
+ Curl_dyn_free(&ctx->data_out);
+ break;
+ }
+
+out:
+ *done = (!result) && (ctx->state == HAPROXY_DONE);
cf->connected = *done;
return result;
}
-static const struct Curl_cftype cft_haproxy = {
+static void cf_haproxy_destroy(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)data;
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ cf_haproxy_ctx_free(cf->ctx);
+}
+
+static void cf_haproxy_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf->connected = FALSE;
+ cf_haproxy_ctx_reset(cf->ctx);
+ if(cf->next)
+ cf->next->cft->close(cf->next, data);
+}
+
+static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ int fds;
+
+ fds = cf->next->cft->get_select_socks(cf->next, data, socks);
+ if(!fds && cf->next->connected && !cf->connected) {
+ /* If we are not connected, but the filter "below" is
+ * and not waiting on something, we are sending. */
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
+ return GETSOCK_WRITESOCK(0);
+ }
+ return fds;
+}
+
+
+struct Curl_cftype Curl_cft_haproxy = {
"HAPROXY",
0,
- Curl_cf_def_destroy_this,
- Curl_cf_def_setup,
- haproxy_cf_connect,
- Curl_cf_def_close,
+ 0,
+ cf_haproxy_destroy,
+ cf_haproxy_connect,
+ cf_haproxy_close,
Curl_cf_def_get_host,
- Curl_cf_def_get_select_socks,
+ cf_haproxy_get_select_socks,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
+static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_haproxy_ctx *ctx;
+ CURLcode result;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->state = HAPROXY_INIT;
+ Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
+
+ result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
+ if(result)
+ goto out;
+ ctx = NULL;
+
+out:
+ cf_haproxy_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
+}
+
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
@@ -1274,10 +1412,28 @@ CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_haproxy, NULL);
- if(!result)
- Curl_conn_cf_add(data, conn, sockindex, cf);
+ result = cf_haproxy_create(&cf, data);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+out:
+ return result;
+}
+
+CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_haproxy_create(&cf, data);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+
+out:
return result;
}
-#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */
+#endif /* !CURL_DISABLE_PROXY */
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h
index dfdc0e72ba..f573da273c 100644
--- a/Utilities/cmcurl/lib/http_proxy.h
+++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,8 +27,9 @@
#include "curl_setup.h"
#include "urldata.h"
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+#if !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP)
/* Default proxy timeout in milliseconds */
#define PROXY_TIMEOUT (3600*1000)
@@ -36,10 +37,22 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_http_proxy;
+
+#endif /* !CURL_DISABLE_HTTP */
+
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_haproxy;
+
+#endif /* !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_HTTP_PROXY_H */
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c
index 6255221ae1..abba895c45 100644
--- a/Utilities/cmcurl/lib/idn.c
+++ b/Utilities/cmcurl/lib/idn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -116,7 +116,7 @@ bool Curl_is_ASCII_name(const char *hostname)
* Curl_idn_decode() returns an allocated IDN decoded string if it was
* possible. NULL on error.
*/
-static char *Curl_idn_decode(const char *input)
+static char *idn_decode(const char *input)
{
char *decoded = NULL;
#ifdef USE_LIBIDN2
@@ -144,24 +144,29 @@ static char *Curl_idn_decode(const char *input)
return decoded;
}
+char *Curl_idn_decode(const char *input)
+{
+ char *d = idn_decode(input);
+#ifdef USE_LIBIDN2
+ if(d) {
+ char *c = strdup(d);
+ idn2_free(d);
+ d = c;
+ }
+#endif
+ return d;
+}
+
/*
* Frees data allocated by idnconvert_hostname()
*/
void Curl_free_idnconverted_hostname(struct hostname *host)
{
-#if defined(USE_LIBIDN2)
if(host->encalloc) {
- idn2_free(host->encalloc); /* must be freed with idn2_free() since this was
- allocated by libidn */
+ /* must be freed with idn2_free() if allocated by libidn */
+ Curl_idn_free(host->encalloc);
host->encalloc = NULL;
}
-#elif defined(USE_WIN32_IDN)
- free(host->encalloc); /* must be freed with free() since this was
- allocated by Curl_win32_idn_to_ascii */
- host->encalloc = NULL;
-#else
- (void)host;
-#endif
}
#endif /* USE_IDN */
@@ -177,7 +182,7 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host)
#ifdef USE_IDN
/* Check name for non-ASCII and convert hostname if we can */
if(!Curl_is_ASCII_name(host->name)) {
- char *decoded = Curl_idn_decode(host->name);
+ char *decoded = idn_decode(host->name);
if(decoded) {
/* successful */
host->encalloc = decoded;
@@ -190,4 +195,3 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host)
#endif
return CURLE_OK;
}
-
diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h
index 2d04efcdff..6c0bbb7109 100644
--- a/Utilities/cmcurl/lib/idn.h
+++ b/Utilities/cmcurl/lib/idn.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -32,7 +32,15 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host);
#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
#define USE_IDN
void Curl_free_idnconverted_hostname(struct hostname *host);
+char *Curl_idn_decode(const char *input);
+#ifdef USE_LIBIDN2
+#define Curl_idn_free(x) idn2_free(x)
+#else
+#define Curl_idn_free(x) free(x)
+#endif
+
#else
#define Curl_free_idnconverted_hostname(x)
+#define Curl_idn_decode(x) NULL
#endif
#endif /* HEADER_CURL_IDN_H */
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c
index c29194878f..6bf0ce16f7 100644
--- a/Utilities/cmcurl/lib/if2ip.c
+++ b/Utilities/cmcurl/lib/if2ip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/if2ip.h b/Utilities/cmcurl/lib/if2ip.h
index 5d15459e98..1f973505c0 100644
--- a/Utilities/cmcurl/lib/if2ip.h
+++ b/Utilities/cmcurl/lib/if2ip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index bd4c6f2203..c2f675d4b2 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -475,15 +475,17 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
/* Start the SSL connection */
struct imap_conn *imapc = &conn->proto.imapc;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ imapc->ssldone = ssldone;
if(imapc->state != IMAP_UPGRADETLS)
state(data, IMAP_UPGRADETLS);
@@ -952,7 +954,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
line += wordlen;
}
}
- else if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* PREAUTH is not compatible with STARTTLS. */
if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
/* Switch to TLS connection now */
@@ -1386,8 +1388,10 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
struct imap_conn *imapc = &conn->proto.imapc;
if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
- if(result || !imapc->ssldone)
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ imapc->ssldone = ssldone;
+ if(result || !ssldone)
return result;
}
@@ -1774,7 +1778,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
/* Calculate the tag based on the connection ID and command ID */
msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
'A' + curlx_sltosi(data->conn->connection_id % 26),
- (++imapc->cmdid)%1000);
+ ++imapc->cmdid);
/* start with a blank buffer */
Curl_dyn_reset(&imapc->dyn);
diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h
index 43cc1e98fc..784ee97e55 100644
--- a/Utilities/cmcurl/lib/imap.h
+++ b/Utilities/cmcurl/lib/imap.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -72,19 +72,19 @@ struct IMAP {
struct */
struct imap_conn {
struct pingpong pp;
- imapstate state; /* Always use imap.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
- bool preauth; /* Is this connection PREAUTH? */
struct SASL sasl; /* SASL-related parameters */
- unsigned int preftype; /* Preferred authentication type */
- unsigned int cmdid; /* Last used command ID */
- char resptag[5]; /* Response tag to wait for */
- bool tls_supported; /* StartTLS capability supported by server */
- bool login_disabled; /* LOGIN command disabled by server */
- bool ir_supported; /* Initial response supported by server */
+ struct dynbuf dyn; /* for the IMAP commands */
char *mailbox; /* The last selected mailbox */
char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */
- struct dynbuf dyn; /* for the IMAP commands */
+ imapstate state; /* Always use imap.c:state() to change state! */
+ char resptag[5]; /* Response tag to wait for */
+ unsigned char preftype; /* Preferred authentication type */
+ unsigned char cmdid; /* Last used command ID */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(preauth); /* Is this connection PREAUTH? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(login_disabled); /* LOGIN command disabled by server */
+ BIT(ir_supported); /* Initial response supported by server */
};
extern const struct Curl_handler Curl_handler_imap;
@@ -96,6 +96,6 @@ extern const struct Curl_handler Curl_handler_imaps;
/* Authentication type values */
#define IMAP_TYPE_NONE 0
-#define IMAP_TYPE_ANY ~0U
+#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL)
#endif /* HEADER_CURL_IMAP_H */
diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h
index 18fbd8ba3a..7c3ead4341 100644
--- a/Utilities/cmcurl/lib/inet_ntop.h
+++ b/Utilities/cmcurl/lib/inet_ntop.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c
index 47fb77834d..f2e17b8743 100644
--- a/Utilities/cmcurl/lib/inet_pton.c
+++ b/Utilities/cmcurl/lib/inet_pton.c
@@ -1,6 +1,6 @@
/* This is from the BIND 4.9.4 release, modified to compile by itself */
-/* Copyright (c) 2003 - 2022 by Internet Software Consortium.
+/* Copyright (c) Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h
index 92ae93ea1f..82fde7e2eb 100644
--- a/Utilities/cmcurl/lib/inet_pton.h
+++ b/Utilities/cmcurl/lib/inet_pton.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c
index 08a682533d..3cd64e110f 100644
--- a/Utilities/cmcurl/lib/krb5.c
+++ b/Utilities/cmcurl/lib/krb5.c
@@ -2,7 +2,7 @@
*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
- * Copyright (c) 2004 - 2022 Daniel Stenberg
+ * Copyright (C) Daniel Stenberg
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
@@ -46,6 +46,8 @@
#endif
#include "urldata.h"
+#include "cfilters.h"
+#include "cf-socket.h"
#include "curl_base64.h"
#include "ftp.h"
#include "curl_gssapi.h"
@@ -207,8 +209,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
gss_ctx_id_t *context = app_data;
struct gss_channel_bindings_struct chan;
size_t base64_sz = 0;
- struct sockaddr_in **remote_addr =
- (struct sockaddr_in **)&conn->ip_addr->ai_addr;
+ struct sockaddr_in *remote_addr =
+ (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr;
char *stringp;
if(getsockname(conn->sock[FIRSTSOCKET],
@@ -220,7 +222,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
chan.acceptor_addrtype = GSS_C_AF_INET;
chan.acceptor_address.length = l - 4;
- chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
+ chan.acceptor_address.value = &remote_addr->sin_addr.s_addr;
chan.application_data.length = 0;
chan.application_data.value = NULL;
@@ -454,15 +456,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
saying whether an error occurred or CURLE_OK if |len| was read. */
static CURLcode
-socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
+socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
{
char *to_p = to;
CURLcode result;
ssize_t nread = 0;
while(len > 0) {
- result = Curl_read_plain(data, fd, to_p, len, &nread);
- if(!result) {
+ nread = Curl_conn_recv(data, sockindex, to_p, len, &result);
+ if(nread > 0) {
len -= nread;
to_p += nread;
}
@@ -480,7 +482,7 @@ socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
CURLcode saying whether an error occurred or CURLE_OK if |len| was
written. */
static CURLcode
-socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
+socket_write(struct Curl_easy *data, int sockindex, const void *to,
size_t len)
{
const char *to_p = to;
@@ -488,8 +490,8 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
ssize_t written;
while(len > 0) {
- result = Curl_write_plain(data, fd, to_p, len, &written);
- if(!result) {
+ written = Curl_conn_send(data, sockindex, to_p, len, &result);
+ if(written > 0) {
len -= written;
to_p += written;
}
@@ -502,7 +504,7 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
return CURLE_OK;
}
-static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
+static CURLcode read_data(struct Curl_easy *data, int sockindex,
struct krb5buffer *buf)
{
struct connectdata *conn = data->conn;
@@ -510,7 +512,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
CURLcode result;
int nread;
- result = socket_read(data, fd, &len, sizeof(len));
+ result = socket_read(data, sockindex, &len, sizeof(len));
if(result)
return result;
@@ -525,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
if(!len || !buf->data)
return CURLE_OUT_OF_MEMORY;
- result = socket_read(data, fd, buf->data, len);
+ result = socket_read(data, sockindex, buf->data, len);
if(result)
return result;
nread = conn->mech->decode(conn->app_data, buf->data, len,
@@ -554,13 +556,12 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
size_t bytes_read;
size_t total_read = 0;
struct connectdata *conn = data->conn;
- curl_socket_t fd = conn->sock[sockindex];
*err = CURLE_OK;
/* Handle clear text response. */
if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
- return Curl_recv_plain(data, sockindex, buffer, len, err);
+ return Curl_conn_recv(data, sockindex, buffer, len, err);
if(conn->in_buffer.eof_flag) {
conn->in_buffer.eof_flag = 0;
@@ -573,7 +574,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
buffer += bytes_read;
while(len > 0) {
- if(read_data(data, fd, &conn->in_buffer))
+ if(read_data(data, sockindex, &conn->in_buffer))
return -1;
if(conn->in_buffer.size == 0) {
if(bytes_read > 0)
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index 92006d6936..5e53f4c511 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc
index 23134a7102..daa2d62d8a 100644
--- a/Utilities/cmcurl/lib/libcurl.rc
+++ b/Utilities/cmcurl/lib/libcurl.rc
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c
index fa2d366cb4..5b6b0336da 100644
--- a/Utilities/cmcurl/lib/llist.c
+++ b/Utilities/cmcurl/lib/llist.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h
index 2fcb91ca50..320580e33c 100644
--- a/Utilities/cmcurl/lib/llist.h
+++ b/Utilities/cmcurl/lib/llist.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index c13b080d69..318e9da6ae 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -86,11 +86,7 @@
#include "memdebug.h"
-#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
-
-#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
-
-#elif defined(USE_GNUTLS)
+#if defined(USE_GNUTLS)
typedef struct md4_ctx MD4_CTX;
@@ -109,6 +105,10 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
md4_digest(ctx, MD4_DIGEST_SIZE, result);
}
+#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+
#elif defined(AN_APPLE_OS)
typedef CC_MD4_CTX MD4_CTX;
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index 9610e0c778..f57ef391cc 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c
index 15fb491559..d6952a07a1 100644
--- a/Utilities/cmcurl/lib/memdebug.c
+++ b/Utilities/cmcurl/lib/memdebug.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h
index 7fc90e83a0..c9eb5dc37c 100644
--- a/Utilities/cmcurl/lib/memdebug.h
+++ b/Utilities/cmcurl/lib/memdebug.h
@@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index e3f2821dbe..83846c57dd 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h
index b9ea0f1011..04adf2d247 100644
--- a/Utilities/cmcurl/lib/mime.h
+++ b/Utilities/cmcurl/lib/mime.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index 8a7c17a7ff..5de935b1fe 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1999 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index 8ba826f4fb..0b54bc0f83 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2019, Björn Stenberg, <bjorn@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Björn Stenberg, <bjorn@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h
index c400d9b14e..63961366fc 100644
--- a/Utilities/cmcurl/lib/mqtt.h
+++ b/Utilities/cmcurl/lib/mqtt.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@haxx.se>
+ * Copyright (C) Björn Stenberg, <bjorn@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index b96ee7c7ea..f020a0b64a 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -655,6 +655,9 @@ static CURLcode multi_done(struct Curl_easy *data,
result = CURLE_ABORTED_BY_CALLBACK;
}
+ /* Inform connection filters that this transfer is done */
+ Curl_conn_ev_data_done(data, premature);
+
process_pending_handles(data->multi); /* connection / multiplex */
CONNCACHE_LOCK(data);
@@ -709,12 +712,12 @@ static CURLcode multi_done(struct Curl_easy *data,
conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
) || conn->bits.close
- || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
+ || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
- ", close=%d, premature=%d, stream=%d",
+ ", close=%d, premature=%d, conn_multiplex=%d",
conn->connection_id,
data->set.reuse_forbid, conn->bits.close, premature,
- (conn->handler->flags & PROTOPT_STREAM)));
+ Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
connclose(conn, "disconnecting");
Curl_conncache_remove_conn(data, conn, FALSE);
CONNCACHE_UNLOCK(data);
@@ -954,7 +957,7 @@ void Curl_detach_connection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
if(conn) {
- Curl_conn_detach_data(conn, data);
+ Curl_conn_ev_data_detach(conn, data);
Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
}
data->conn = NULL;
@@ -973,9 +976,9 @@ void Curl_attach_connection(struct Curl_easy *data,
data->conn = conn;
Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
&data->conn_queue);
- Curl_conn_attach_data(conn, data);
if(conn->handler->attach)
conn->handler->attach(data, conn);
+ Curl_conn_ev_data_attach(conn, data);
}
static int domore_getsock(struct Curl_easy *data,
@@ -1002,11 +1005,7 @@ static int protocol_getsock(struct Curl_easy *data,
{
if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(data, conn, socks);
- /* Backup getsock logic. Since there is a live socket in use, we must wait
- for it or it will be removed from watching when the multi_socket API is
- used. */
- socks[0] = conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
+ return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
}
/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
@@ -1111,6 +1110,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
return CURLM_OK;
}
+#ifdef USE_WINSOCK
+/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't
+ * be reset this way because an empty datagram would be sent. #9203
+ *
+ * "On Windows the internal state of FD_WRITE as returned from
+ * WSAEnumNetworkEvents is only reset after successful send()."
+ */
+static void reset_socket_fdwrite(curl_socket_t s)
+{
+ int t;
+ int l = (int)sizeof(t);
+ if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM)
+ send(s, NULL, 0, 0);
+}
+#endif
+
#define NUM_POLLS_ON_STACK 10
static CURLMcode multi_wait(struct Curl_multi *multi,
@@ -1232,7 +1247,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
s = sockbunch[i];
#ifdef USE_WINSOCK
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- send(s, NULL, 0, 0); /* reset FD_WRITE */
+ reset_socket_fdwrite(s);
#endif
ufds[nfds].fd = s;
ufds[nfds].events = POLLOUT;
@@ -1266,7 +1281,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
mask |= FD_OOB;
if(extra_fds[i].events & CURL_WAIT_POLLOUT) {
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */
+ reset_socket_fdwrite(extra_fds[i].fd);
}
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) {
if(ufds_malloc)
@@ -1862,6 +1877,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
multistate(data, MSTATE_COMPLETED);
}
+#ifdef DEBUGBUILD
+ if(!multi->warned) {
+ infof(data, "!!! WARNING !!!");
+ infof(data, "This is a debug build of libcurl, "
+ "do not use in production.");
+ multi->warned = true;
+ }
+#endif
+
do {
/* A "stream" here is a logical stream if the protocol can handle that
(HTTP/2), or the full connection for older protocols */
@@ -3248,7 +3272,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
multi->push_userp = va_arg(param, void *);
break;
case CURLMOPT_PIPELINING:
- multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX;
+ multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0;
break;
case CURLMOPT_TIMERFUNCTION:
multi->timer_cb = va_arg(param, curl_multi_timer_callback);
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index 5a83656d51..6cda65d442 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -162,14 +162,17 @@ struct Curl_multi {
#define IPV6_DEAD 1
#define IPV6_WORKS 2
unsigned char ipv6_up; /* IPV6_* defined */
- bool multiplexing; /* multiplexing wanted */
- bool recheckstate; /* see Curl_multi_connchanged */
- bool in_callback; /* true while executing a callback */
+ BIT(multiplexing); /* multiplexing wanted */
+ BIT(recheckstate); /* see Curl_multi_connchanged */
+ BIT(in_callback); /* true while executing a callback */
#ifdef USE_OPENSSL
- bool ssl_seeded;
+ BIT(ssl_seeded);
#endif
- bool dead; /* a callback returned error, everything needs to crash and
+ BIT(dead); /* a callback returned error, everything needs to crash and
burn */
+#ifdef DEBUGBUILD
+ BIT(warned); /* true after user warned of DEBUGBUILD */
+#endif
};
#endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h
index 0cb9d4f7f2..cae02cb08c 100644
--- a/Utilities/cmcurl/lib/multiif.h
+++ b/Utilities/cmcurl/lib/multiif.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 4461b8492f..aa1b80ab2f 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h
index 53d0056721..9f2815f3bb 100644
--- a/Utilities/cmcurl/lib/netrc.h
+++ b/Utilities/cmcurl/lib/netrc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/nonblock.c b/Utilities/cmcurl/lib/nonblock.c
index 8447b6f423..f4eb656128 100644
--- a/Utilities/cmcurl/lib/nonblock.c
+++ b/Utilities/cmcurl/lib/nonblock.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/nonblock.h b/Utilities/cmcurl/lib/nonblock.h
index a42f443a49..4a1a6151f2 100644
--- a/Utilities/cmcurl/lib/nonblock.h
+++ b/Utilities/cmcurl/lib/nonblock.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c
index 9b13fe8956..f1c1ed2c63 100644
--- a/Utilities/cmcurl/lib/noproxy.c
+++ b/Utilities/cmcurl/lib/noproxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -119,8 +119,10 @@ enum nametype {
* 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)
+bool Curl_check_noproxy(const char *name, const char *no_proxy,
+ bool *spacesep)
{
+ *spacesep = FALSE;
/*
* If we don't have a hostname at all, like for example with a FILE
* transfer, we have nothing to interrogate the noproxy list with.
@@ -244,6 +246,15 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
if(match)
return TRUE;
} /* if(tokenlen) */
+ /* pass blanks after pattern */
+ while(ISBLANK(*p))
+ p++;
+ /* if not a comma! */
+ if(*p && (*p != ',')) {
+ *spacesep = TRUE;
+ continue;
+ }
+ /* pass any number of commas */
while(*p == ',')
p++;
} /* while(*p) */
diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h
index 8800a21276..a3a6807722 100644
--- a/Utilities/cmcurl/lib/noproxy.h
+++ b/Utilities/cmcurl/lib/noproxy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,7 +37,8 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6,
unsigned int bits);
#endif
-bool Curl_check_noproxy(const char *name, const char *no_proxy);
+bool Curl_check_noproxy(const char *name, const char *no_proxy,
+ bool *spacesep);
#endif
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index ab81e57c26..b9feeda058 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Howard Chu, <hyc@openldap.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c
index 5ed88195fb..82ac1d8928 100644
--- a/Utilities/cmcurl/lib/parsedate.c
+++ b/Utilities/cmcurl/lib/parsedate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/parsedate.h b/Utilities/cmcurl/lib/parsedate.h
index 4e4347754d..84c37f1675 100644
--- a/Utilities/cmcurl/lib/parsedate.h
+++ b/Utilities/cmcurl/lib/parsedate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 9b9558048a..2f4aa1c346 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h
index cefae073a6..80d3f7718c 100644
--- a/Utilities/cmcurl/lib/pingpong.h
+++ b/Utilities/cmcurl/lib/pingpong.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index ce17f2ac72..36707e5352 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -370,16 +370,18 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
/* Start the SSL connection */
struct pop3_conn *pop3c = &conn->proto.pop3c;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ pop3c->ssldone = ssldone;
if(pop3c->state != POP3_UPGRADETLS)
state(data, POP3_UPGRADETLS);
@@ -769,7 +771,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
if(pop3code != '+')
pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
- if(!data->set.use_ssl || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = pop3_perform_authentication(data, conn);
else if(pop3code == '+' && pop3c->tls_supported)
/* Switch to TLS connection now */
@@ -1056,7 +1058,9 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
struct pop3_conn *pop3c = &conn->proto.pop3c;
if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ pop3c->ssldone = ssldone;
if(result || !pop3c->ssldone)
return result;
}
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h
index bb0645f9bf..83f0f831e6 100644
--- a/Utilities/cmcurl/lib/pop3.h
+++ b/Utilities/cmcurl/lib/pop3.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,16 +62,16 @@ struct POP3 {
struct pop3_conn {
struct pingpong pp;
pop3state state; /* Always use pop3.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
- bool tls_supported; /* StartTLS capability supported by server */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
size_t strip; /* Number of bytes from the start to ignore as
non-body */
struct SASL sasl; /* SASL-related storage */
- unsigned int authtypes; /* Accepted authentication types */
- unsigned int preftype; /* Preferred authentication type */
char *apoptimestamp; /* APOP timestamp from the server greeting */
+ unsigned char authtypes; /* Accepted authentication types */
+ unsigned char preftype; /* Preferred authentication type */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
};
extern const struct Curl_handler Curl_handler_pop3;
@@ -84,7 +84,7 @@ extern const struct Curl_handler Curl_handler_pop3s;
/* Authentication type values */
#define POP3_TYPE_NONE 0
-#define POP3_TYPE_ANY ~0U
+#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
/* This is the 5-bytes End-Of-Body marker for POP3 */
#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index 4a1e1daa81..a2228888e6 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -166,14 +166,11 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data)
/*
*
- * Curl_pgrsTime(). Store the current time at the given label. This fetches a
- * fresh "now" and returns it.
- *
- * @unittest: 1399
+ * Curl_pgrsTimeWas(). Store the timestamp time at the given label.
*/
-struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
+ struct curltime timestamp)
{
- struct curltime now = Curl_now();
timediff_t *delta = NULL;
switch(timer) {
@@ -183,15 +180,15 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
break;
case TIMER_STARTOP:
/* This is set at the start of a transfer */
- data->progress.t_startop = now;
+ data->progress.t_startop = timestamp;
break;
case TIMER_STARTSINGLE:
/* This is set at the start of each single fetch */
- data->progress.t_startsingle = now;
+ data->progress.t_startsingle = timestamp;
data->progress.is_t_startransfer_set = false;
break;
case TIMER_STARTACCEPT:
- data->progress.t_acceptdata = now;
+ data->progress.t_acceptdata = timestamp;
break;
case TIMER_NAMELOOKUP:
delta = &data->progress.t_nslookup;
@@ -214,7 +211,7 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
* changing the t_starttransfer time.
*/
if(data->progress.is_t_startransfer_set) {
- return now;
+ return;
}
else {
data->progress.is_t_startransfer_set = true;
@@ -224,15 +221,30 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
/* this is the normal end-of-transfer thing */
break;
case TIMER_REDIRECT:
- data->progress.t_redirect = Curl_timediff_us(now, data->progress.start);
+ data->progress.t_redirect = Curl_timediff_us(timestamp,
+ data->progress.start);
break;
}
if(delta) {
- timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle);
+ timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle);
if(us < 1)
us = 1; /* make sure at least one microsecond passed */
*delta += us;
}
+}
+
+/*
+ *
+ * Curl_pgrsTime(). Store the current time at the given label. This fetches a
+ * fresh "now" and returns it.
+ *
+ * @unittest: 1399
+ */
+struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+{
+ struct curltime now = Curl_now();
+
+ Curl_pgrsTimeWas(data, timer, now);
return now;
}
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h
index a129315147..0049cd04be 100644
--- a/Utilities/cmcurl/lib/progress.h
+++ b/Utilities/cmcurl/lib/progress.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -57,6 +57,13 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
curl_off_t limit,
struct curltime start,
struct curltime now);
+/**
+ * Update progress timer with the elapsed time from its start to `timestamp`.
+ * This allows updating timers later and is used by happy eyeballing, where
+ * we only want to record the winner's times.
+ */
+void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
+ struct curltime timestamp);
#define PGRS_HIDE (1<<4)
#define PGRS_UL_SIZE_KNOWN (1<<5)
diff --git a/Utilities/cmcurl/lib/psl.c b/Utilities/cmcurl/lib/psl.c
index 60c98a4ca4..626a203a6d 100644
--- a/Utilities/cmcurl/lib/psl.c
+++ b/Utilities/cmcurl/lib/psl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/psl.h b/Utilities/cmcurl/lib/psl.h
index 34f0a5cae8..23cfa921c4 100644
--- a/Utilities/cmcurl/lib/psl.h
+++ b/Utilities/cmcurl/lib/psl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/quic.h b/Utilities/cmcurl/lib/quic.h
deleted file mode 100644
index b35774735e..0000000000
--- a/Utilities/cmcurl/lib/quic.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef HEADER_CURL_QUIC_H
-#define HEADER_CURL_QUIC_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef ENABLE_QUIC
-#ifdef USE_NGTCP2
-#include "vquic/ngtcp2.h"
-#endif
-#ifdef USE_QUICHE
-#include "vquic/quiche.h"
-#endif
-#ifdef USE_MSH3
-#include "vquic/msh3.h"
-#endif
-
-#include "urldata.h"
-
-/* functions provided by the specific backends */
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen);
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected);
-void Curl_quic_ver(char *p, size_t len);
-CURLcode Curl_quic_done_sending(struct Curl_easy *data);
-void Curl_quic_done(struct Curl_easy *data, bool premature);
-bool Curl_quic_data_pending(const struct Curl_easy *data);
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn, int tempindex);
-CURLcode Curl_quic_idle(struct Curl_easy *data);
-
-#else /* ENABLE_QUIC */
-#define Curl_quic_done_sending(x)
-#define Curl_quic_done(x,y)
-#define Curl_quic_data_pending(x)
-#define Curl_quic_disconnect(x,y,z)
-#endif /* !ENABLE_QUIC */
-
-#endif /* HEADER_CURL_QUIC_H */
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index a549624a86..4b6ac072e6 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index 30fc29615a..cbe05677a1 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c
index cfb3699fb7..97a66e947e 100644
--- a/Utilities/cmcurl/lib/rename.c
+++ b/Utilities/cmcurl/lib/rename.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rename.h b/Utilities/cmcurl/lib/rename.h
index 9958e2cd23..04440820c5 100644
--- a/Utilities/cmcurl/lib/rename.h
+++ b/Utilities/cmcurl/lib/rename.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index 75e620d118..9d27929e84 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -38,6 +38,7 @@
#include "strcase.h"
#include "select.h"
#include "connect.h"
+#include "cfilters.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -134,36 +135,6 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data,
/*
- * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
- * want to block the application forever while receiving a stream. Therefore,
- * we cannot assume that an RTSP socket is dead just because it is readable.
- *
- * Instead, if it is readable, run Curl_connalive() to peek at the socket
- * and distinguish between closed and data.
- */
-static bool rtsp_connisdead(struct Curl_easy *data, struct connectdata *check)
-{
- int sval;
- bool ret_val = TRUE;
-
- sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
- if(sval == 0) {
- /* timeout */
- ret_val = FALSE;
- }
- else if(sval & CURL_CSELECT_ERR) {
- /* socket is in an error state */
- ret_val = TRUE;
- }
- else if(sval & CURL_CSELECT_IN) {
- /* readable with no error. could still be closed */
- ret_val = !Curl_connalive(data, check);
- }
-
- return ret_val;
-}
-
-/*
* Function to check on various aspects of a connection.
*/
static unsigned int rtsp_conncheck(struct Curl_easy *data,
@@ -174,7 +145,7 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data,
(void)data;
if(checks_to_perform & CONNCHECK_ISDEAD) {
- if(rtsp_connisdead(data, conn))
+ if(!Curl_conn_is_alive(data, conn))
ret_val |= CONNRESULT_DEAD;
}
@@ -592,7 +563,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
}
/* issue the request */
- result = Curl_buffer_send(&req_buffer, data,
+ result = Curl_buffer_send(&req_buffer, data, data->req.p.http,
&data->info.request_size, 0, FIRSTSOCKET);
if(result) {
failf(data, "Failed sending RTSP request");
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h
index fa6606ac92..6e55616b38 100644
--- a/Utilities/cmcurl/lib/rtsp.h
+++ b/Utilities/cmcurl/lib/rtsp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c
index 2ac0746772..3b8d468bd8 100644
--- a/Utilities/cmcurl/lib/select.c
+++ b/Utilities/cmcurl/lib/select.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h
index f2cf8bbd9f..5b1ca23eb1 100644
--- a/Utilities/cmcurl/lib/select.h
+++ b/Utilities/cmcurl/lib/select.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index 63262409bb..2b08271687 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -138,149 +138,6 @@ static size_t convert_lineends(struct Curl_easy *data,
}
#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
- struct postponed_data * const psnd = &(conn->postponed[sockindex]);
- return psnd->buffer && psnd->allocated_size &&
- psnd->recv_size > psnd->recv_processed;
-}
-
-static CURLcode pre_receive_plain(struct Curl_easy *data,
- struct connectdata *conn, int num)
-{
- const curl_socket_t sockfd = conn->sock[num];
- struct postponed_data * const psnd = &(conn->postponed[num]);
- size_t bytestorecv = psnd->allocated_size - psnd->recv_size;
- ssize_t recvedbytes;
-
- /* WinSock will destroy unread received data if send() is
- failed.
- To avoid lossage of received data, recv() must be
- performed before every send() if any incoming data is
- available. However, skip this, if buffer is already full. */
- if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
- conn->recv[num] == Curl_conn_recv &&
- (!psnd->buffer || bytestorecv)) {
- const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD,
- CURL_SOCKET_BAD, 0);
- if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
- /* Have some incoming data */
- if(!psnd->buffer) {
- /* Use buffer double default size for intermediate buffer */
- psnd->allocated_size = 2 * data->set.buffer_size;
- psnd->buffer = malloc(psnd->allocated_size);
- if(!psnd->buffer)
- return CURLE_OUT_OF_MEMORY;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
- bytestorecv = psnd->allocated_size;
- }
-
- DEBUGASSERT(psnd->bindsock == sockfd);
- recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size,
- bytestorecv);
- if(recvedbytes > 0)
- psnd->recv_size += recvedbytes;
- }
- }
- return CURLE_OK;
-}
-
-static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf,
- size_t len)
-{
- struct postponed_data * const psnd = &(conn->postponed[num]);
- size_t copysize;
- if(!psnd->buffer)
- return 0;
-
- DEBUGASSERT(psnd->allocated_size > 0);
- DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
- DEBUGASSERT(psnd->recv_processed <= psnd->recv_size);
- /* Check and process data that already received and storied in internal
- intermediate buffer */
- if(psnd->recv_size > psnd->recv_processed) {
- DEBUGASSERT(psnd->bindsock == conn->sock[num]);
- copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed);
- memcpy(buf, psnd->buffer + psnd->recv_processed, copysize);
- psnd->recv_processed += copysize;
- }
- else
- copysize = 0; /* buffer was allocated, but nothing was received */
-
- /* Free intermediate buffer if it has no unprocessed data */
- if(psnd->recv_processed == psnd->recv_size) {
- free(psnd->buffer);
- psnd->buffer = NULL;
- psnd->allocated_size = 0;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = CURL_SOCKET_BAD;
-#endif /* DEBUGBUILD */
- }
- return (ssize_t)copysize;
-}
-#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macros instead of functions when workaround not used */
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
- (void)conn;
- (void)sockindex;
- return false;
-}
-#define pre_receive_plain(d,c,n) CURLE_OK
-#define get_pre_recved(c,n,b,l) 0
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
-/* Curl_infof() is for info message along the way */
-#define MAXINFO 2048
-
-void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
-{
- DEBUGASSERT(!strchr(fmt, '\n'));
- if(data && data->set.verbose) {
- va_list ap;
- int len;
- char buffer[MAXINFO + 2];
- va_start(ap, fmt);
- len = mvsnprintf(buffer, MAXINFO, fmt, ap);
- va_end(ap);
- buffer[len++] = '\n';
- buffer[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, buffer, len);
- }
-}
-
-/* Curl_failf() is for messages stating why we failed.
- * The message SHALL NOT include any LF or CR.
- */
-
-void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
-{
- DEBUGASSERT(!strchr(fmt, '\n'));
- if(data->set.verbose || data->set.errorbuffer) {
- va_list ap;
- int len;
- char error[CURL_ERROR_SIZE + 2];
- va_start(ap, fmt);
- len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
-
- if(data->set.errorbuffer && !data->state.errorbuf) {
- strcpy(data->set.errorbuffer, error);
- data->state.errorbuf = TRUE; /* wrote error string */
- }
- error[len++] = '\n';
- error[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, error, len);
- va_end(ap);
- }
-}
-
/*
* Curl_write() is an internal write function that sends data to the
* server. Works with plain sockets, SCP, SSL or kerberos.
@@ -301,7 +158,7 @@ CURLcode Curl_write(struct Curl_easy *data,
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
conn = data->conn;
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
+ num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
#ifdef CURLDEBUG
{
@@ -338,153 +195,6 @@ CURLcode Curl_write(struct Curl_easy *data,
}
}
-/* Curl_send_plain sends raw data without a size restriction on 'len'. */
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
- const void *mem, size_t len, CURLcode *code)
-{
- struct connectdata *conn;
- curl_socket_t sockfd;
- ssize_t bytes_written;
-
- DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- conn = data->conn;
- sockfd = conn->sock[num];
- /* WinSock will destroy unread received data if send() is
- failed.
- To avoid lossage of received data, recv() must be
- performed before every send() if any incoming data is
- available. */
- if(pre_receive_plain(data, conn, num)) {
- *code = CURLE_OUT_OF_MEMORY;
- return -1;
- }
-
-#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
- if(conn->bits.tcp_fastopen) {
- bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN,
- conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen);
- conn->bits.tcp_fastopen = FALSE;
- }
- else
-#endif
- bytes_written = swrite(sockfd, mem, len);
-
- *code = CURLE_OK;
- if(-1 == bytes_written) {
- int err = SOCKERRNO;
-
- if(
-#ifdef WSAEWOULDBLOCK
- /* This is how Windows does it */
- (WSAEWOULDBLOCK == err)
-#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 */
- (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) ||
- (EINPROGRESS == err)
-#endif
- ) {
- /* this is just a case of EWOULDBLOCK */
- *code = CURLE_AGAIN;
- }
- else {
- char buffer[STRERROR_LEN];
- failf(data, "Send failure: %s",
- Curl_strerror(err, buffer, sizeof(buffer)));
- data->state.os_errno = err;
- *code = CURLE_SEND_ERROR;
- }
- }
- return bytes_written;
-}
-
-/*
- * Curl_write_plain() is an internal write function that sends data to the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_write().
- *
- * This function wraps Curl_send_plain(). The only difference besides the
- * prototype is '*written' (bytes written) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'len' must not be changed.
- */
-CURLcode Curl_write_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- const void *mem,
- size_t len,
- ssize_t *written)
-{
- CURLcode result;
- struct connectdata *conn = data->conn;
- int num;
- DEBUGASSERT(conn);
- DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
- sockfd == conn->sock[SECONDARYSOCKET]);
- if(sockfd != conn->sock[FIRSTSOCKET] &&
- sockfd != conn->sock[SECONDARYSOCKET])
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
- *written = Curl_send_plain(data, num, mem, len, &result);
- if(*written == -1)
- *written = 0;
-
- return result;
-}
-
-/* Curl_recv_plain receives raw data without a size restriction on 'len'. */
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
- size_t len, CURLcode *code)
-{
- struct connectdata *conn;
- curl_socket_t sockfd;
- ssize_t nread;
- DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- conn = data->conn;
- sockfd = conn->sock[num];
- /* Check and return data that already received and storied in internal
- intermediate buffer */
- nread = get_pre_recved(conn, num, buf, len);
- if(nread > 0) {
- *code = CURLE_OK;
- return nread;
- }
-
- nread = sread(sockfd, buf, len);
-
- *code = CURLE_OK;
- if(-1 == nread) {
- int err = SOCKERRNO;
-
- if(
-#ifdef WSAEWOULDBLOCK
- /* This is how Windows does it */
- (WSAEWOULDBLOCK == err)
-#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 */
- (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
-#endif
- ) {
- /* this is just a case of EWOULDBLOCK */
- *code = CURLE_AGAIN;
- }
- else {
- char buffer[STRERROR_LEN];
- failf(data, "Recv failure: %s",
- Curl_strerror(err, buffer, sizeof(buffer)));
- data->state.os_errno = err;
- *code = CURLE_RECV_ERROR;
- }
- }
- return nread;
-}
-
static CURLcode pausewrite(struct Curl_easy *data,
int type, /* what type of data */
const char *ptr,
@@ -498,8 +208,7 @@ static CURLcode pausewrite(struct Curl_easy *data,
unsigned int i;
bool newtype = TRUE;
- /* If this transfers over HTTP/2, pause the stream! */
- Curl_http2_stream_pause(data, TRUE);
+ Curl_conn_ev_data_pause(data, TRUE);
if(s->tempcount) {
for(i = 0; i< s->tempcount; i++) {
@@ -678,41 +387,6 @@ CURLcode Curl_client_write(struct Curl_easy *data,
}
/*
- * Curl_read_plain() is an internal read function that reads data from the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_read().
- *
- * This function wraps Curl_recv_plain(). The only difference besides the
- * prototype is '*n' (bytes read) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'sizerequested' must not be changed.
- */
-CURLcode Curl_read_plain(struct Curl_easy *data, /* transfer */
- curl_socket_t sockfd, /* read from this socket */
- char *buf, /* store read data here */
- size_t sizerequested, /* max amount to read */
- ssize_t *n) /* amount bytes read */
-{
- CURLcode result;
- struct connectdata *conn = data->conn;
- int num;
- DEBUGASSERT(conn);
- DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
- sockfd == conn->sock[SECONDARYSOCKET]);
- if(sockfd != conn->sock[FIRSTSOCKET] &&
- sockfd != conn->sock[SECONDARYSOCKET])
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
- *n = Curl_recv_plain(data, num, buf, sizerequested, &result);
- if(*n == -1)
- *n = 0;
-
- return result;
-}
-
-/*
* Internal read-from-socket function. This is meant to deal with plain
* sockets, SSL sockets and kerberos sockets.
*
@@ -752,30 +426,3 @@ out:
return result;
}
-/* return 0 on success */
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size)
-{
- if(data->set.verbose) {
- static const char s_infotype[CURLINFO_END][3] = {
- "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
- if(data->set.fdebug) {
- bool inCallback = Curl_is_in_callback(data);
- Curl_set_in_callback(data, true);
- (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
- }
- else {
- switch(type) {
- case CURLINFO_TEXT:
- case CURLINFO_HEADER_OUT:
- case CURLINFO_HEADER_IN:
- fwrite(s_infotype[type], 2, 1, data->set.err);
- fwrite(ptr, size, 1, data->set.err);
- break;
- default: /* nada */
- break;
- }
- }
- }
-}
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h
index 8af5c46085..d0c9275705 100644
--- a/Utilities/cmcurl/lib/sendf.h
+++ b/Utilities/cmcurl/lib/sendf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,26 +26,8 @@
#include "curl_setup.h"
-void Curl_infof(struct Curl_easy *, const char *fmt, ...);
-void Curl_failf(struct Curl_easy *, const char *fmt, ...);
+#include "curl_log.h"
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-
-#if defined(HAVE_VARIADIC_MACROS_C99)
-#define infof(...) Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC)
-#define infof(x...) Curl_nop_stmt
-#else
-#error "missing VARIADIC macro define, fix and rebuild!"
-#endif
-
-#else /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define infof Curl_infof
-
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define failf Curl_failf
#define CLIENTWRITE_BODY (1<<0)
#define CLIENTWRITE_HEADER (1<<1)
@@ -58,20 +40,6 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...);
CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
size_t len) WARN_UNUSED_RESULT;
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex);
-
-/* internal read-function, does plain socket only */
-CURLcode Curl_read_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- char *buf,
- size_t sizerequested,
- ssize_t *n);
-
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
- size_t len, CURLcode *code);
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
- const void *mem, size_t len, CURLcode *code);
-
/* internal read-function, does plain socket, SSL and krb4 */
CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
char *buf, size_t buffersize,
@@ -83,15 +51,4 @@ CURLcode Curl_write(struct Curl_easy *data,
const void *mem, size_t len,
ssize_t *written);
-/* internal write-function, does plain sockets ONLY */
-CURLcode Curl_write_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- const void *mem, size_t len,
- ssize_t *written);
-
-/* the function used to output verbose information */
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size);
-
-
#endif /* HEADER_CURL_SENDF_H */
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index b77e95b4e3..604693ad94 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -174,7 +174,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val)
*val |= h->protocol;
}
- } while(str++);
+ } while(str && str++);
if(!*val)
/* no protocol listed */
@@ -463,8 +463,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
version_max >= CURL_SSLVERSION_MAX_LAST)
return CURLE_BAD_FUNCTION_ARGUMENT;
- primary->version = version;
- primary->version_max = version_max;
+ primary->version = (unsigned char)version;
+ primary->version_max = (unsigned int)version_max;
}
#else
result = CURLE_NOT_BUILT_IN;
@@ -732,13 +732,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
break;
- case CURLOPT_HTTP200ALIASES:
- /*
- * Set a list of aliases for HTTP 200 in response header
- */
- data->set.http200aliases = va_arg(param, struct curl_slist *);
- break;
-
#if !defined(CURL_DISABLE_COOKIES)
case CURLOPT_COOKIE:
/*
@@ -760,18 +753,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_BAD_FUNCTION_ARGUMENT;
/* append the cookie file name to the list of file names, and deal with
them later */
- cl = curl_slist_append(data->state.cookielist, argptr);
+ cl = curl_slist_append(data->set.cookielist, argptr);
if(!cl) {
- curl_slist_free_all(data->state.cookielist);
- data->state.cookielist = NULL;
+ curl_slist_free_all(data->set.cookielist);
+ data->set.cookielist = NULL;
return CURLE_OUT_OF_MEMORY;
}
- data->state.cookielist = cl; /* store the list for later use */
+ data->set.cookielist = cl; /* store the list for later use */
}
else {
/* clear the list of cookie files */
- curl_slist_free_all(data->state.cookielist);
- data->state.cookielist = NULL;
+ curl_slist_free_all(data->set.cookielist);
+ data->set.cookielist = NULL;
if(!data->share || !data->share->cookies) {
/* throw away all existing cookies if this isn't a shared cookie
@@ -902,22 +895,38 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* the listed enums in curl/curl.h.
*/
arg = va_arg(param, long);
- if(arg < CURL_HTTP_VERSION_NONE)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ switch(arg) {
+ case CURL_HTTP_VERSION_NONE:
+#ifdef USE_HTTP2
+ /* TODO: this seems an undesirable quirk to force a behaviour on
+ * lower implementations that they should recognize independantly? */
+ arg = CURL_HTTP_VERSION_2TLS;
+#endif
+ /* accepted */
+ break;
+ case CURL_HTTP_VERSION_1_0:
+ case CURL_HTTP_VERSION_1_1:
+ /* accepted */
+ break;
+#ifdef USE_HTTP2
+ case CURL_HTTP_VERSION_2_0:
+ case CURL_HTTP_VERSION_2TLS:
+ case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+ /* accepted */
+ break;
+#endif
#ifdef ENABLE_QUIC
- if(arg == CURL_HTTP_VERSION_3)
- ;
- else
+ case CURL_HTTP_VERSION_3:
+ case CURL_HTTP_VERSION_3ONLY:
+ /* accepted */
+ break;
#endif
-#ifndef USE_HTTP2
- if(arg >= CURL_HTTP_VERSION_2)
- return CURLE_UNSUPPORTED_PROTOCOL;
-#else
- if(arg >= CURL_HTTP_VERSION_LAST)
+ default:
+ /* not accepted */
+ if(arg < CURL_HTTP_VERSION_NONE)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
return CURLE_UNSUPPORTED_PROTOCOL;
- if(arg == CURL_HTTP_VERSION_NONE)
- arg = CURL_HTTP_VERSION_2TLS;
-#endif
+ }
data->set.httpwant = (unsigned char)arg;
break;
@@ -944,6 +953,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.http09_allowed = arg ? TRUE : FALSE;
#endif
break;
+
+ case CURLOPT_HTTP200ALIASES:
+ /*
+ * Set a list of aliases for HTTP 200 in response header
+ */
+ data->set.http200aliases = va_arg(param, struct curl_slist *);
+ break;
#endif /* CURL_DISABLE_HTTP */
#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
@@ -1309,6 +1325,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
break;
#endif
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
case CURLOPT_FTP_CREATE_MISSING_DIRS:
/*
* An FTP/SFTP option that modifies an upload to create missing
@@ -1322,6 +1339,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
else
data->set.ftp_create_missing_dirs = (unsigned char)arg;
break;
+
+ case CURLOPT_POSTQUOTE:
+ /*
+ * List of RAW FTP commands to use after a transfer
+ */
+ data->set.postquote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_PREQUOTE:
+ /*
+ * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
+ */
+ data->set.prequote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_QUOTE:
+ /*
+ * List of RAW FTP commands to use before a transfer
+ */
+ data->set.quote = va_arg(param, struct curl_slist *);
+ break;
+#endif
case CURLOPT_READDATA:
/*
* FILE pointer to read the file to be uploaded from. Or possibly
@@ -1431,7 +1468,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_TIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.timeout = (unsigned int)uarg;
break;
@@ -1449,7 +1486,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_CONNECTTIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.connecttimeout = (unsigned int)uarg;
break;
@@ -1460,7 +1497,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* The maximum time for curl to wait for FTP server connect
*/
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.accepttimeout = (unsigned int)uarg;
break;
@@ -1506,24 +1543,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
- case CURLOPT_POSTQUOTE:
- /*
- * List of RAW FTP commands to use after a transfer
- */
- data->set.postquote = va_arg(param, struct curl_slist *);
- break;
- case CURLOPT_PREQUOTE:
- /*
- * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
- */
- data->set.prequote = va_arg(param, struct curl_slist *);
- break;
- case CURLOPT_QUOTE:
- /*
- * List of RAW FTP commands to use before a transfer
- */
- data->set.quote = va_arg(param, struct curl_slist *);
- break;
case CURLOPT_RESOLVE:
/*
* List of HOST:PORT:[addresses] strings to populate the DNS cache with
@@ -1871,16 +1890,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = va_arg(param, long);
if((arg < 0) || (arg > 65535))
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.localportrange = curlx_sltosi(arg);
+ data->set.localportrange = curlx_sltous(arg);
break;
case CURLOPT_GSSAPI_DELEGATION:
/*
* GSS-API credential delegation bitmask
*/
- arg = va_arg(param, long);
- if(arg < CURLGSSAPI_DELEGATION_NONE)
- return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.gssapi_delegation = arg;
+ uarg = va_arg(param, unsigned long);
+ data->set.gssapi_delegation = (unsigned char)uarg&
+ (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG);
break;
case CURLOPT_SSL_VERIFYPEER:
/*
@@ -2260,9 +2278,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->cookies = NULL;
#endif
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts == data->hsts)
+ data->hsts = NULL;
+#endif
+#ifdef USE_SSL
if(data->share->sslsession == data->state.session)
data->state.session = NULL;
-
+#endif
#ifdef USE_LIBPSL
if(data->psl == &data->share->psl)
data->psl = data->multi? &data->multi->psl: NULL;
@@ -2296,10 +2319,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->cookies = data->share->cookies;
}
#endif /* CURL_DISABLE_HTTP */
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts) {
+ /* first free the private one if any */
+ Curl_hsts_cleanup(&data->hsts);
+ data->hsts = data->share->hsts;
+ }
+#endif /* CURL_DISABLE_HTTP */
+#ifdef USE_SSL
if(data->share->sslsession) {
data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
data->state.session = data->share->sslsession;
}
+#endif
#ifdef USE_LIBPSL
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
data->psl = &data->share->psl;
@@ -2515,6 +2547,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
+ case CURLOPT_SSH_KNOWNHOSTS:
+ /*
+ * Store the file name to read known hosts from.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
+ va_arg(param, char *));
+ break;
+#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
/*
* Option to allow for the SHA256 of the host public key to be checked
@@ -2524,14 +2564,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
- case CURLOPT_SSH_KNOWNHOSTS:
- /*
- * Store the file name to read known hosts from.
- */
- result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
- va_arg(param, char *));
- break;
-#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOSTKEYFUNCTION:
/* the callback to check the hostkey without the knownhost file */
data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback);
@@ -2544,6 +2576,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.ssh_hostkeyfunc_userp = va_arg(param, void *);
break;
#endif
+
case CURLOPT_SSH_KEYFUNCTION:
/* setting to NULL is fine since the ssh.c functions themselves will
then revert to use the internal default */
@@ -2590,7 +2623,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.new_file_perms = (unsigned int)arg;
break;
-
+#endif
+#ifdef USE_SSH
case CURLOPT_NEW_DIRECTORY_PERMS:
/*
* Uses these permissions instead of 0755
@@ -2825,52 +2859,33 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_TLSAUTH_USERNAME:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME] &&
- !data->set.ssl.primary.authtype)
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_USERNAME:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
- !data->set.proxy_ssl.primary.authtype)
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to
- SRP */
break;
#endif
case CURLOPT_TLSAUTH_PASSWORD:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME] &&
- !data->set.ssl.primary.authtype)
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_PASSWORD:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
- !data->set.proxy_ssl.primary.authtype)
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
break;
#endif
case CURLOPT_TLSAUTH_TYPE:
argptr = va_arg(param, char *);
- if(!argptr ||
- strncasecompare(argptr, "SRP", strlen("SRP")))
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP;
- else
- data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP")))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_TYPE:
argptr = va_arg(param, char *);
- if(!argptr ||
- strncasecompare(argptr, "SRP", strlen("SRP")))
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP;
- else
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP")))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
break;
#endif
#endif
@@ -2956,29 +2971,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
case CURLOPT_STREAM_WEIGHT:
-#ifndef USE_NGHTTP2
- return CURLE_NOT_BUILT_IN;
-#else
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
arg = va_arg(param, long);
if((arg >= 1) && (arg <= 256))
- data->set.stream_weight = (int)arg;
+ data->set.priority.weight = (int)arg;
break;
+#else
+ return CURLE_NOT_BUILT_IN;
#endif
case CURLOPT_STREAM_DEPENDS:
case CURLOPT_STREAM_DEPENDS_E:
{
-#ifndef USE_NGHTTP2
- return CURLE_NOT_BUILT_IN;
-#else
struct Curl_easy *dep = va_arg(param, struct Curl_easy *);
if(!dep || GOOD_EASY_HANDLE(dep)) {
- if(data->set.stream_depends_on) {
- Curl_http2_remove_child(data->set.stream_depends_on, data);
- }
- Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E));
+ return Curl_data_priority_add_child(dep, data,
+ option == CURLOPT_STREAM_DEPENDS_E);
}
break;
-#endif
}
case CURLOPT_CONNECT_TO:
data->set.connect_to = va_arg(param, struct curl_slist *);
@@ -2988,7 +2997,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
break;
case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.happy_eyeballs_timeout = (unsigned int)uarg;
break;
@@ -3049,19 +3058,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_HSTSWRITEDATA:
data->set.hsts_write_userp = va_arg(param, void *);
break;
- case CURLOPT_HSTS:
+ case CURLOPT_HSTS: {
+ struct curl_slist *h;
if(!data->hsts) {
data->hsts = Curl_hsts_init();
if(!data->hsts)
return CURLE_OUT_OF_MEMORY;
}
argptr = va_arg(param, char *);
- result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
- if(result)
- return result;
- if(argptr)
- (void)Curl_hsts_loadfile(data, data->hsts, argptr);
+ if(argptr) {
+ result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
+ if(result)
+ return result;
+ /* this needs to build a list of file names to read from, so that it can
+ read them later, as we might get a shared HSTS handle to load them
+ into */
+ h = curl_slist_append(data->set.hstslist, argptr);
+ if(!h) {
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->set.hstslist = h; /* store the list for later use */
+ }
+ else {
+ /* clear the list of HSTS files */
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ if(!data->share || !data->share->hsts)
+ /* throw away the HSTS cache unless shared */
+ Curl_hsts_cleanup(&data->hsts);
+ }
break;
+ }
case CURLOPT_HSTS_CTRL:
arg = va_arg(param, long);
if(arg & CURLHSTS_ENABLE) {
diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h
index ffc77a71dc..3c14a05e37 100644
--- a/Utilities/cmcurl/lib/setopt.h
+++ b/Utilities/cmcurl/lib/setopt.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h
index 7854397860..759583466c 100644
--- a/Utilities/cmcurl/lib/setup-os400.h
+++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -205,7 +205,7 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status,
extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen);
extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen);
extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags,
- struct sockaddr *dstaddr, int addrlen);
+ const struct sockaddr *dstaddr, int addrlen);
extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags,
struct sockaddr *fromaddr, int *addrlen);
extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen);
diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h
index b570683d78..46657b2cd4 100644
--- a/Utilities/cmcurl/lib/setup-vms.h
+++ b/Utilities/cmcurl/lib/setup-vms.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h
index bc5f8efc3c..13948389a4 100644
--- a/Utilities/cmcurl/lib/setup-win32.h
+++ b/Utilities/cmcurl/lib/setup-win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c
index c96a9fc61f..fdfd631ef9 100644
--- a/Utilities/cmcurl/lib/sha256.c
+++ b/Utilities/cmcurl/lib/sha256.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c
index 1a083e72a0..c0a8d806f3 100644
--- a/Utilities/cmcurl/lib/share.c
+++ b/Utilities/cmcurl/lib/share.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,9 +29,11 @@
#include "share.h"
#include "psl.h"
#include "vtls/vtls.h"
-#include "curl_memory.h"
+#include "hsts.h"
-/* The last #include file should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
#include "memdebug.h"
struct Curl_share *
@@ -89,6 +91,18 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(!share->hsts) {
+ share->hsts = Curl_hsts_init();
+ if(!share->hsts)
+ res = CURLSHE_NOMEM;
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
if(!share->sslsession) {
@@ -141,6 +155,16 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(share->hsts) {
+ Curl_hsts_cleanup(&share->hsts);
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
Curl_safefree(share->sslsession);
@@ -207,6 +231,10 @@ curl_share_cleanup(struct Curl_share *share)
Curl_cookie_cleanup(share->cookies);
#endif
+#ifndef CURL_DISABLE_HSTS
+ Curl_hsts_cleanup(&share->hsts);
+#endif
+
#ifdef USE_SSL
if(share->sslsession) {
size_t i;
diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h
index 32be41691a..7f55aac853 100644
--- a/Utilities/cmcurl/lib/share.h
+++ b/Utilities/cmcurl/lib/share.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,10 +59,14 @@ struct Curl_share {
#ifdef USE_LIBPSL
struct PslCache psl;
#endif
-
+#ifndef CURL_DISABLE_HSTS
+ struct hsts *hsts;
+#endif
+#ifdef USE_SSL
struct Curl_ssl_session *sslsession;
size_t max_ssl_sessions;
long sessionage;
+#endif
};
CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h
index d12b31764d..14ab25b67c 100644
--- a/Utilities/cmcurl/lib/sigpipe.h
+++ b/Utilities/cmcurl/lib/sigpipe.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c
index 6c80722c77..366b247609 100644
--- a/Utilities/cmcurl/lib/slist.c
+++ b/Utilities/cmcurl/lib/slist.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/slist.h b/Utilities/cmcurl/lib/slist.h
index 4e5834c90a..9561fd0226 100644
--- a/Utilities/cmcurl/lib/slist.h
+++ b/Utilities/cmcurl/lib/slist.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 48d5a2fe00..dc0abe784b 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -763,6 +763,11 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
void *msg = NULL;
const struct smb_nt_create_response *smb_m;
+ if(data->set.upload && (data->state.infilesize < 0)) {
+ failf(data, "SMB upload needs to know the size up front");
+ return CURLE_SEND_ERROR;
+ }
+
/* Start the request */
if(req->state == SMB_REQUESTING) {
result = smb_send_tree_connect(data);
@@ -993,6 +998,7 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data,
/* The share must be present */
if(!slash) {
Curl_safefree(smbc->share);
+ failf(data, "missing share in URL path for SMB");
return CURLE_URL_MALFORMAT;
}
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h
index 919f3ac142..c35f3e9700 100644
--- a/Utilities/cmcurl/lib/smb.h
+++ b/Utilities/cmcurl/lib/smb.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 6d0783f458..7a030308d4 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -398,15 +398,17 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
struct connectdata *conn = data->conn;
struct smtp_conn *smtpc = &conn->proto.smtpc;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ smtpc->ssldone = ssldone;
if(smtpc->state != SMTP_UPGRADETLS)
state(data, SMTP_UPGRADETLS);
@@ -891,7 +893,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
if(smtpcode/100 != 2 && smtpcode != 1) {
if(data->set.use_ssl <= CURLUSESSL_TRY
- || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ || Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = smtp_perform_helo(data, conn);
else {
failf(data, "Remote access denied: %d", smtpcode);
@@ -956,7 +958,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
}
if(smtpcode != 1) {
- if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* We don't have a SSL/TLS connection yet, but SSL is requested */
if(smtpc->tls_supported)
/* Switch to TLS connection now */
@@ -1288,7 +1290,9 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
struct smtp_conn *smtpc = &conn->proto.smtpc;
if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ smtpc->ssldone = ssldone;
if(result || !smtpc->ssldone)
return result;
}
diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h
index 24c5589e43..7a04c21549 100644
--- a/Utilities/cmcurl/lib/smtp.h
+++ b/Utilities/cmcurl/lib/smtp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -57,28 +57,28 @@ struct SMTP {
curl_pp_transfer transfer;
char *custom; /* Custom Request */
struct curl_slist *rcpt; /* Recipient list */
- bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on
- total number of recipients) succeeded so far */
- bool trailing_crlf; /* Specifies if the trailing CRLF is present */
int rcpt_last_error; /* The last error received for RCPT TO command */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
+ BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on
+ total number of recipients) succeeded so far */
+ BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */
};
/* smtp_conn is used for struct connection-oriented data in the connectdata
struct */
struct smtp_conn {
struct pingpong pp;
+ struct SASL sasl; /* SASL-related storage */
smtpstate state; /* Always use smtp.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
char *domain; /* Client address/name to send in the EHLO */
- struct SASL sasl; /* SASL-related storage */
- bool tls_supported; /* StartTLS capability supported by server */
- bool size_supported; /* If server supports SIZE extension according to
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(size_supported); /* If server supports SIZE extension according to
RFC 1870 */
- bool utf8_supported; /* If server supports SMTPUTF8 extension according
+ BIT(utf8_supported); /* If server supports SMTPUTF8 extension according
to RFC 6531 */
- bool auth_supported; /* AUTH capability supported by server */
+ BIT(auth_supported); /* AUTH capability supported by server */
};
extern const struct Curl_handler Curl_handler_smtp;
diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h
index 77ec833ee0..5a6bb207dc 100644
--- a/Utilities/cmcurl/lib/sockaddr.h
+++ b/Utilities/cmcurl/lib/sockaddr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c
index 0f8798f087..b94c9843e6 100644
--- a/Utilities/cmcurl/lib/socketpair.c
+++ b/Utilities/cmcurl/lib/socketpair.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -65,7 +65,7 @@ int Curl_socketpair(int domain, int type, int protocol,
union {
struct sockaddr_in inaddr;
struct sockaddr addr;
- } a, a2;
+ } a;
curl_socket_t listener;
curl_socklen_t addrlen = sizeof(a.inaddr);
int reuse = 1;
@@ -85,9 +85,22 @@ int Curl_socketpair(int domain, int type, int protocol,
socks[0] = socks[1] = CURL_SOCKET_BAD;
+#if defined(WIN32) || defined(__CYGWIN__)
+ /* don't set SO_REUSEADDR on Windows */
+ (void)reuse;
+#ifdef SO_EXCLUSIVEADDRUSE
+ {
+ int exclusive = 1;
+ if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1)
+ goto error;
+ }
+#endif
+#else
if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
goto error;
+#endif
if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
goto error;
if(getsockname(listener, &a.addr, &addrlen) == -1 ||
@@ -107,24 +120,59 @@ int Curl_socketpair(int domain, int type, int protocol,
pfd[0].fd = listener;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
- (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */
+ (void)Curl_poll(pfd, 1, 1000); /* one second */
socks[1] = accept(listener, NULL, NULL);
if(socks[1] == CURL_SOCKET_BAD)
goto error;
+ else {
+ struct curltime check;
+ struct curltime start = Curl_now();
+ char *p = (char *)&check;
+ size_t s = sizeof(check);
- /* verify that nothing else connected */
- addrlen = sizeof(a.inaddr);
- if(getsockname(socks[0], &a.addr, &addrlen) == -1 ||
- addrlen < (int)sizeof(a.inaddr))
- goto error;
- addrlen = sizeof(a2.inaddr);
- if(getpeername(socks[1], &a2.addr, &addrlen) == -1 ||
- addrlen < (int)sizeof(a2.inaddr))
- goto error;
- if(a.inaddr.sin_family != a2.inaddr.sin_family ||
- a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr ||
- a.inaddr.sin_port != a2.inaddr.sin_port)
- goto error;
+ /* write data to the socket */
+ swrite(socks[0], &start, sizeof(start));
+ /* verify that we read the correct data */
+ do {
+ ssize_t nread;
+
+ pfd[0].fd = socks[1];
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ (void)Curl_poll(pfd, 1, 1000); /* one second */
+
+ nread = sread(socks[1], p, s);
+ if(nread == -1) {
+ int sockerr = SOCKERRNO;
+ /* Don't block forever */
+ if(Curl_timediff(Curl_now(), start) > (60 * 1000))
+ goto error;
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == 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 */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
+ (EINTR == sockerr) || (EINPROGRESS == sockerr)
+#endif
+ ) {
+ continue;
+ }
+ goto error;
+ }
+ s -= nread;
+ if(s) {
+ p += nread;
+ continue;
+ }
+ if(memcmp(&start, &check, sizeof(check)))
+ goto error;
+ break;
+ } while(1);
+ }
sclose(listener);
return 0;
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h
index de70df673a..306ab5dc4c 100644
--- a/Utilities/cmcurl/lib/socketpair.h
+++ b/Utilities/cmcurl/lib/socketpair.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index d491e089be..95c2b004c9 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -89,8 +89,8 @@ struct socks_state {
*
* This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
*/
-int Curl_blockread_all(struct Curl_easy *data, /* transfer */
- curl_socket_t sockfd, /* read from this socket */
+int Curl_blockread_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
char *buf, /* store read data here */
ssize_t buffersize, /* max amount to read */
ssize_t *n) /* amount bytes read */
@@ -98,6 +98,8 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */
ssize_t nread = 0;
ssize_t allread = 0;
int result;
+ CURLcode err = CURLE_OK;
+
*n = 0;
for(;;) {
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -108,15 +110,19 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */
}
if(!timeout_ms)
timeout_ms = TIMEDIFF_T_MAX;
- if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
+ if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) {
result = ~CURLE_OK;
break;
}
- result = Curl_read_plain(data, sockfd, buf, buffersize, &nread);
- if(CURLE_AGAIN == result)
- continue;
- if(result)
- break;
+ nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err);
+ if(nread <= 0) {
+ result = err;
+ if(CURLE_AGAIN == err)
+ continue;
+ if(err) {
+ break;
+ }
+ }
if(buffersize == nread) {
allread += nread;
@@ -192,6 +198,68 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data,
#endif
}
+static CURLproxycode socks_state_send(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data,
+ CURLproxycode failcode,
+ const char *description)
+{
+ ssize_t nwritten;
+ CURLcode result;
+
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
+ sx->outstanding, &result);
+ if(nwritten <= 0) {
+ if(CURLE_AGAIN == result) {
+ return CURLPX_OK;
+ }
+ else if(CURLE_OK == result) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ failf(data, "Failed to send %s: %s", description,
+ curl_easy_strerror(result));
+ return failcode;
+ }
+ DEBUGASSERT(sx->outstanding >= nwritten);
+ /* not done, remain in state */
+ sx->outstanding -= nwritten;
+ sx->outp += nwritten;
+ return CURLPX_OK;
+}
+
+static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data,
+ CURLproxycode failcode,
+ const char *description)
+{
+ ssize_t nread;
+ CURLcode result;
+
+ nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
+ sx->outstanding, &result);
+ if(nread <= 0) {
+ if(CURLE_AGAIN == result) {
+ return CURLPX_OK;
+ }
+ else if(CURLE_OK == result) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ failf(data, "SOCKS4: Failed receiving %s: %s", description,
+ curl_easy_strerror(result));
+ return failcode;
+ }
+ /* remain in reading state */
+ DEBUGASSERT(sx->outstanding >= nread);
+ sx->outstanding -= nread;
+ sx->outp += nread;
+ return CURLPX_OK;
+}
+
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
@@ -212,10 +280,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
unsigned char *socksreq = (unsigned char *)data->state.buffer;
CURLcode result;
- curl_socket_t sockfd = conn->sock[cf->sockindex];
+ CURLproxycode presult;
struct Curl_dns_entry *dns = NULL;
- ssize_t actualread;
- ssize_t written;
/* make sure that the buffer is at least 600 bytes */
DEBUGASSERT(READBUFFER_MIN >= 600);
@@ -250,7 +316,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* DNS resolve only for SOCKS4, not SOCKS4a */
if(!protocol4a) {
enum resolve_t rc =
- Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns);
+ Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
@@ -375,19 +441,14 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
/* Send request */
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS4 connect request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != sx->outstanding) {
- /* not done, remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "SOCKS4 connect request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
-
/* done sending! */
sx->outstanding = 8; /* receive data size */
sx->outp = socksreq;
@@ -396,22 +457,12 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
/* Receive response */
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "SOCKS4: Failed receiving connect request ack: %s",
- curl_easy_strerror(result));
- return CURLPX_RECV_CONNECT;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+ "connect request ack");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
/* remain in reading state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_DONE);
@@ -518,10 +569,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
unsigned char *socksreq = (unsigned char *)data->state.buffer;
char dest[256] = "unknown"; /* printable hostname:port */
int idx;
- ssize_t actualread;
- ssize_t written;
CURLcode result;
- curl_socket_t sockfd = conn->sock[cf->sockindex];
+ CURLproxycode presult;
bool socks5_resolve_local =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
const size_t hostname_len = strlen(sx->hostname);
@@ -567,30 +616,25 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
/* write the number of authentication methods */
socksreq[1] = (unsigned char) (idx - 2);
- result = Curl_write_plain(data, sockfd, socksreq, idx, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to send initial SOCKS5 request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != idx) {
- sxstate(sx, data, CONNECT_SOCKS_SEND);
- sx->outstanding = idx - written;
- sx->outp = &socksreq[written];
+ sx->outp = socksreq;
+ sx->outstanding = idx;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "initial SOCKS5 request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_SOCKS_READ);
goto CONNECT_SOCKS_READ_INIT;
case CONNECT_SOCKS_SEND:
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to send initial SOCKS5 request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != sx->outstanding) {
- /* not done, remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "initial SOCKS5 request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
/* FALLTHROUGH */
@@ -600,21 +644,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sx->outp = socksreq; /* store it here */
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to receive initial SOCKS5 response.");
- return CURLPX_RECV_CONNECT;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "Connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+ "initial SOCKS5 response");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
/* remain in reading state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
return CURLPX_OK;
}
else if(socksreq[0] != 5) {
@@ -634,7 +669,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else if(allow_gssapi && (socksreq[1] == 1)) {
sxstate(sx, data, CONNECT_GSSAPI_INIT);
- result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data);
+ result = Curl_SOCKS5_gssapi_negotiate(cf, data);
if(result) {
failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
return CURLPX_GSSAPI;
@@ -713,16 +748,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
}
/* FALLTHROUGH */
case CONNECT_AUTH_SEND:
- result = Curl_write_plain(data, sockfd, sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS5 sub-negotiation request.");
- return CURLPX_SEND_AUTH;
- }
- if(sx->outstanding != written) {
- /* remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
+ "SOCKS5 sub-negotiation request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
sx->outp = socksreq;
@@ -730,21 +761,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_AUTH_READ);
/* FALLTHROUGH */
case CONNECT_AUTH_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
- return CURLPX_RECV_AUTH;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
+ "SOCKS5 sub-negotiation response");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
/* ignore the first (VER) byte */
@@ -761,7 +783,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
case CONNECT_REQ_INIT:
if(socks5_resolve_local) {
enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
- FALSE, &dns);
+ TRUE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
@@ -909,16 +931,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_REQ_SENDING);
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS5 connect request.");
- return CURLPX_SEND_REQUEST;
- }
- if(sx->outstanding != written) {
- /* remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
+ "SOCKS5 connect request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in send state */
return CURLPX_OK;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@@ -932,25 +950,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_REQ_READ);
/* FALLTHROUGH */
case CONNECT_REQ_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to receive SOCKS5 connect request ack.");
- return CURLPX_RECV_REQACK;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
+ "SOCKS5 connect request ack");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
-
- if(socksreq[0] != 5) { /* version */
+ else if(socksreq[0] != 5) { /* version */
failf(data,
"SOCKS5 reply has wrong version, version should be 5.");
return CURLPX_BAD_VERSION;
@@ -1031,21 +1039,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
#endif
/* FALLTHROUGH */
case CONNECT_REQ_READ_MORE:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to receive SOCKS5 connect request ack.");
- return CURLPX_RECV_ADDRESS;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
+ "SOCKS5 connect request address");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_DONE);
@@ -1151,7 +1150,6 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
result = connect_SOCKS(cf, sx, data);
if(!result && sx->state == CONNECT_DONE) {
cf->connected = TRUE;
- Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]);
Curl_verboseconnect(data, conn);
socks_proxy_cf_free(cf);
}
@@ -1171,7 +1169,7 @@ static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
if(!fds && cf->next->connected && !cf->connected && sx) {
/* If we are not connected, the filter below is and has nothing
* to wait on, we determine what to wait for. */
- socks[0] = cf->conn->sock[cf->sockindex];
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
switch(sx->state) {
case CONNECT_RESOLVING:
case CONNECT_SOCKS_READ:
@@ -1205,13 +1203,6 @@ static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
socks_proxy_cf_free(cf);
}
-static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)data;
- socks_proxy_cf_free(cf);
-}
-
static void socks_cf_get_host(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char **phost,
@@ -1229,11 +1220,11 @@ static void socks_cf_get_host(struct Curl_cfilter *cf,
}
}
-static const struct Curl_cftype cft_socks_proxy = {
+struct Curl_cftype Curl_cft_socks_proxy = {
"SOCKS-PROXYY",
CF_TYPE_IP_CONNECT,
+ 0,
socks_proxy_cf_destroy,
- Curl_cf_def_setup,
socks_proxy_cf_connect,
socks_proxy_cf_close,
socks_cf_get_host,
@@ -1241,8 +1232,10 @@ static const struct Curl_cftype cft_socks_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- socks_proxy_cf_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
@@ -1252,10 +1245,23 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_socks_proxy, NULL);
+ result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
+CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ (void)data;
+ result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
#endif /* CURL_DISABLE_PROXY */
diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h
index 2e2fa18f81..ba5b54a7bd 100644
--- a/Utilities/cmcurl/lib/socks.h
+++ b/Utilities/cmcurl/lib/socks.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,8 +37,8 @@
*
* This is STUPID BLOCKING behavior
*/
-int Curl_blockread_all(struct Curl_easy *data,
- curl_socket_t sockfd,
+int Curl_blockread_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
char *buf,
ssize_t buffersize,
ssize_t *n);
@@ -47,7 +47,7 @@ int Curl_blockread_all(struct Curl_easy *data,
/*
* This function handles the SOCKS5 GSS-API negotiation and initialization
*/
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data);
#endif
@@ -55,6 +55,11 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_socks_proxy;
+
#endif /* CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_SOCKS_H */
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index f14099febf..2ede8c7c29 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,6 +30,7 @@
#include "curl_gssapi.h"
#include "urldata.h"
#include "sendf.h"
+#include "cfilters.h"
#include "connect.h"
#include "timeval.h"
#include "socks.h"
@@ -101,14 +102,14 @@ static int check_gss_err(struct Curl_easy *data,
return 0;
}
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- curl_socket_t sock = conn->sock[sockindex];
+ struct connectdata *conn = cf->conn;
+ curl_socket_t sock = conn->sock[cf->sockindex];
CURLcode code;
ssize_t actualread;
- ssize_t written;
+ ssize_t nwritten;
int result;
OM_uint32 gss_major_status, gss_minor_status, gss_status;
OM_uint32 gss_ret_flags;
@@ -203,8 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
us_length = htons((short)gss_send_token.length);
memcpy(socksreq + 2, &us_length, sizeof(short));
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
- if(code || (4 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API authentication request.");
gss_release_name(&gss_status, &server);
gss_release_buffer(&gss_status, &gss_recv_token);
@@ -213,10 +214,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
- code = Curl_write_plain(data, sock, (char *)gss_send_token.value,
- gss_send_token.length, &written);
-
- if(code || ((ssize_t)gss_send_token.length != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data,
+ (char *)gss_send_token.value,
+ gss_send_token.length, &code);
+ if(code || ((ssize_t)gss_send_token.length != nwritten)) {
failf(data, "Failed to send GSS-API authentication token.");
gss_release_name(&gss_status, &server);
gss_release_buffer(&gss_status, &gss_recv_token);
@@ -242,7 +243,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
* +----+------+-----+----------------+
*/
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ 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);
@@ -281,7 +282,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
gss_recv_token.length, &actualread);
if(result || (actualread != us_length)) {
@@ -410,8 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
- if(code || (4 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API encryption request.");
gss_release_buffer(&gss_status, &gss_w_token);
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -420,17 +421,18 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- code = Curl_write_plain(data, sock, socksreq, 1, &written);
- if(code || ( 1 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
+ if(code || ( 1 != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_COULDNT_CONNECT;
}
}
else {
- code = Curl_write_plain(data, sock, (char *)gss_w_token.value,
- gss_w_token.length, &written);
- if(code || ((ssize_t)gss_w_token.length != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data,
+ (char *)gss_w_token.value,
+ gss_w_token.length, &code);
+ if(code || ((ssize_t)gss_w_token.length != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_release_buffer(&gss_status, &gss_w_token);
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -439,7 +441,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
gss_release_buffer(&gss_status, &gss_w_token);
}
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive GSS-API encryption response.");
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -470,7 +472,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
gss_recv_token.length, &actualread);
if(result || (actualread != us_length)) {
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index 210a0dfbc5..d1200ea037 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,6 +29,7 @@
#include "urldata.h"
#include "sendf.h"
+#include "cfilters.h"
#include "connect.h"
#include "strerror.h"
#include "timeval.h"
@@ -62,11 +63,11 @@ static int check_sspi_err(struct Curl_easy *data,
}
/* This is the SSPI-using version of this function */
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- curl_socket_t sock = conn->sock[sockindex];
+ struct connectdata *conn = cf->conn;
+ curl_socket_t sock = conn->sock[cf->sockindex];
CURLcode code;
ssize_t actualread;
ssize_t written;
@@ -206,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
us_length = htons((short)sspi_send_token.cbBuffer);
memcpy(socksreq + 2, &us_length, sizeof(short));
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
if(code || (4 != written)) {
failf(data, "Failed to send SSPI authentication request.");
free(service_name);
@@ -219,8 +220,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
- code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &written);
+ written = Curl_conn_cf_send(cf->next, data,
+ (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &code);
if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
failf(data, "Failed to send SSPI authentication token.");
free(service_name);
@@ -260,7 +262,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
* +----+------+-----+----------------+
*/
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI authentication response.");
free(service_name);
@@ -300,7 +302,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
s_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer,
+ result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
sspi_recv_token.cbBuffer, &actualread);
if(result || (actualread != us_length)) {
@@ -468,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
if(code || (4 != written)) {
failf(data, "Failed to send SSPI encryption request.");
if(sspi_send_token.pvBuffer)
@@ -479,7 +481,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
if(code || (1 != written)) {
failf(data, "Failed to send SSPI encryption type.");
s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -487,8 +489,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
}
}
else {
- code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &written);
+ written = Curl_conn_cf_send(cf->next, data,
+ (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &code);
if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
failf(data, "Failed to send SSPI encryption type.");
if(sspi_send_token.pvBuffer)
@@ -500,7 +503,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
}
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI encryption response.");
s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -532,7 +535,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer,
+ result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer,
sspi_w_token[0].cbBuffer, &actualread);
if(result || (actualread != us_length)) {
diff --git a/Utilities/cmcurl/lib/speedcheck.c b/Utilities/cmcurl/lib/speedcheck.c
index 3ddc43d2de..580efbde75 100644
--- a/Utilities/cmcurl/lib/speedcheck.c
+++ b/Utilities/cmcurl/lib/speedcheck.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/speedcheck.h b/Utilities/cmcurl/lib/speedcheck.h
index cb44eb04ec..bff2f32b77 100644
--- a/Utilities/cmcurl/lib/speedcheck.h
+++ b/Utilities/cmcurl/lib/speedcheck.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c
index 33b44aa1c6..48e079b32a 100644
--- a/Utilities/cmcurl/lib/splay.c
+++ b/Utilities/cmcurl/lib/splay.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/splay.h b/Utilities/cmcurl/lib/splay.h
index 015e2ca52f..dd1d07ac2e 100644
--- a/Utilities/cmcurl/lib/splay.h
+++ b/Utilities/cmcurl/lib/splay.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c
index 7fb9c80870..7c0b4ef909 100644
--- a/Utilities/cmcurl/lib/strcase.c
+++ b/Utilities/cmcurl/lib/strcase.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strcase.h b/Utilities/cmcurl/lib/strcase.h
index 192e0da099..8c50bbcba6 100644
--- a/Utilities/cmcurl/lib/strcase.h
+++ b/Utilities/cmcurl/lib/strcase.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c
index ac22b6ddaf..07a61391ae 100644
--- a/Utilities/cmcurl/lib/strdup.c
+++ b/Utilities/cmcurl/lib/strdup.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,7 +37,7 @@
#include "memdebug.h"
#ifndef HAVE_STRDUP
-char *curlx_strdup(const char *str)
+char *Curl_strdup(const char *str)
{
size_t len;
char *newstr;
diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h
index fb46808b83..c3430b54d5 100644
--- a/Utilities/cmcurl/lib/strdup.h
+++ b/Utilities/cmcurl/lib/strdup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,7 +26,7 @@
#include "curl_setup.h"
#ifndef HAVE_STRDUP
-extern char *curlx_strdup(const char *str);
+char *Curl_strdup(const char *str);
#endif
#ifdef WIN32
wchar_t* Curl_wcsdup(const wchar_t* src);
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index b9a51e26b9..3ec10e3695 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2004 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -550,6 +550,9 @@ curl_url_strerror(CURLUcode error)
case CURLUE_BAD_USER:
return "Bad user";
+ case CURLUE_LACKS_IDN:
+ return "libcurl lacks IDN support";
+
case CURLUE_LAST:
break;
}
diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h
index 658f16c10e..399712f8eb 100644
--- a/Utilities/cmcurl/lib/strerror.h
+++ b/Utilities/cmcurl/lib/strerror.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtok.c b/Utilities/cmcurl/lib/strtok.c
index 6120bcc28e..d8e1e8183f 100644
--- a/Utilities/cmcurl/lib/strtok.c
+++ b/Utilities/cmcurl/lib/strtok.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtok.h b/Utilities/cmcurl/lib/strtok.h
index 641a3daed8..321cba2326 100644
--- a/Utilities/cmcurl/lib/strtok.h
+++ b/Utilities/cmcurl/lib/strtok.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c
index fb8d92196f..077b25792e 100644
--- a/Utilities/cmcurl/lib/strtoofft.c
+++ b/Utilities/cmcurl/lib/strtoofft.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtoofft.h b/Utilities/cmcurl/lib/strtoofft.h
index 311dae4403..34d293ba38 100644
--- a/Utilities/cmcurl/lib/strtoofft.h
+++ b/Utilities/cmcurl/lib/strtoofft.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c
index bede9c7dcd..0cdaf3b2fe 100644
--- a/Utilities/cmcurl/lib/system_win32.c
+++ b/Utilities/cmcurl/lib/system_win32.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h
index 167804e3c5..24899cb2d5 100644
--- a/Utilities/cmcurl/lib/system_win32.h
+++ b/Utilities/cmcurl/lib/system_win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 22bc81e755..a964bfdcde 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h
index 6dd99b48dc..30782d8375 100644
--- a/Utilities/cmcurl/lib/telnet.h
+++ b/Utilities/cmcurl/lib/telnet.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index 9e6d9490ed..164d3c723c 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,6 +48,7 @@
#include "urldata.h"
#include <curl/curl.h>
+#include "cf-socket.h"
#include "transfer.h"
#include "sendf.h"
#include "tftp.h"
@@ -529,8 +530,8 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
not have a size_t argument, like older unixes that want an 'int' */
senddata = sendto(state->sockfd, (void *)state->spacket.data,
(SEND_TYPE_ARG3)sbytes, 0,
- data->conn->ip_addr->ai_addr,
- data->conn->ip_addr->ai_addrlen);
+ &data->conn->remote_addr->sa_addr,
+ data->conn->remote_addr->addrlen);
if(senddata != (ssize_t)sbytes) {
char buffer[STRERROR_LEN];
failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
@@ -1014,7 +1015,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
state->requested_blksize = blksize;
((struct sockaddr *)&state->local_addr)->sa_family =
- (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family);
+ (CURL_SA_FAMILY_T)(conn->remote_addr->family);
tftp_set_timeouts(state);
@@ -1033,7 +1034,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
* IPv4 and IPv6...
*/
int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
- conn->ip_addr->ai_addrlen);
+ conn->remote_addr->addrlen);
if(rc) {
char buffer[STRERROR_LEN];
failf(data, "bind() failed; %s",
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h
index 3f1fda6382..5d2d5da615 100644
--- a/Utilities/cmcurl/lib/tftp.h
+++ b/Utilities/cmcurl/lib/tftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timediff.c b/Utilities/cmcurl/lib/timediff.c
index c5893187dd..1b762bbd3e 100644
--- a/Utilities/cmcurl/lib/timediff.c
+++ b/Utilities/cmcurl/lib/timediff.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timediff.h b/Utilities/cmcurl/lib/timediff.h
index 90e547457f..fb318d4f2b 100644
--- a/Utilities/cmcurl/lib/timediff.h
+++ b/Utilities/cmcurl/lib/timediff.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c
index 647d7b0fc5..dca1c6fe50 100644
--- a/Utilities/cmcurl/lib/timeval.c
+++ b/Utilities/cmcurl/lib/timeval.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h
index 8d4fef4e17..92e484ad1d 100644
--- a/Utilities/cmcurl/lib/timeval.h
+++ b/Utilities/cmcurl/lib/timeval.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index ba0410fc51..69df214ce4 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,6 +73,7 @@
#include "url.h"
#include "getinfo.h"
#include "vtls/vtls.h"
+#include "vquic/vquic.h"
#include "select.h"
#include "multiif.h"
#include "connect.h"
@@ -367,27 +368,12 @@ static int data_pending(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC)
- return Curl_quic_data_pending(data);
-#endif
-
if(conn->handler->protocol&PROTO_FAMILY_FTP)
return Curl_conn_data_pending(data, SECONDARYSOCKET);
/* in the case of libssh2, we can never be really sure that we have emptied
its internal buffers so we MUST always try until we get EAGAIN back */
return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
-#ifdef USE_NGHTTP2
- /* For HTTP/2, we may read up everything including response body
- with header fields in Curl_http_readwrite_headers. If no
- content-length is provided, curl waits for the connection
- close, which we emulate it using conn->proto.httpc.closed =
- TRUE. The thing is if we read everything, then http2_recv won't
- be called and we cannot signal the HTTP/2 stream has closed. As
- a workaround, we return nonzero here to call http2_recv. */
- ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) ||
-#endif
Curl_conn_data_pending(data, FIRSTSOCKET);
}
@@ -454,29 +440,16 @@ static CURLcode readwrite_data(struct Curl_easy *data,
bool is_empty_data = FALSE;
size_t buffersize = data->set.buffer_size;
size_t bytestoread = buffersize;
-#ifdef USE_NGHTTP2
- bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- (conn->httpversion == 20));
-#endif
- bool is_http3 =
-#ifdef ENABLE_QUIC
- ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- (conn->httpversion == 30));
-#else
- FALSE;
-#endif
-
- if(
-#ifdef USE_NGHTTP2
- /* For HTTP/2, read data without caring about the content length. This
- is safe because body in HTTP/2 is always segmented thanks to its
- framing layer. Meanwhile, we have to call Curl_read to ensure that
- http2_handle_stream_close is called when we read all incoming bytes
- for a particular stream. */
- !is_http2 &&
-#endif
- !is_http3 && /* Same reason mentioned above. */
- k->size != -1 && !k->header) {
+ /* For HTTP/2 and HTTP/3, read data without caring about the content
+ length. This is safe because body in HTTP/2 is always segmented
+ thanks to its framing layer. Meanwhile, we have to call Curl_read
+ to ensure that http2_handle_stream_close is called when we read all
+ incoming bytes for a particular stream. */
+ bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
+ bool data_eof_handled = is_http3
+ || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
+
+ if(!data_eof_handled && k->size != -1 && !k->header) {
/* make sure we don't read too much */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
@@ -499,7 +472,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
else {
/* read nothing but since we wanted nothing we consider this an OK
situation to proceed from */
- DEBUGF(infof(data, "readwrite_data: we're done"));
+ DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done")));
nread = 0;
}
@@ -518,14 +491,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
buf[nread] = 0;
}
else {
- /* if we receive 0 or less here, either the http2 stream is closed or the
+ /* if we receive 0 or less here, either the data transfer is done or the
server closed the connection and we bail out from this! */
-#ifdef USE_NGHTTP2
- if(is_http2 && !nread)
- DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
- else
-#endif
- if(is_http3 && !nread)
+ if(data_eof_handled)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
@@ -776,8 +744,8 @@ static CURLcode readwrite_data(struct Curl_easy *data,
k->keepon &= ~KEEP_RECV;
}
- if(k->keepon & KEEP_RECV_PAUSE) {
- /* this is a paused transfer */
+ if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
+ /* this is a paused or stopped transfer */
break;
}
@@ -799,19 +767,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
}
out:
- DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result));
+ if(result)
+ DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result));
return result;
}
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k)
{
- struct connectdata *conn = data->conn;
k->keepon &= ~KEEP_SEND; /* we're done writing */
/* These functions should be moved into the handler struct! */
- Curl_http2_done_sending(data, conn);
- Curl_quic_done_sending(data);
+ Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}
@@ -1088,6 +1055,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
{
struct SingleRequest *k = &data->req;
CURLcode result;
+ struct curltime now;
int didwhat = 0;
curl_socket_t fd_read;
@@ -1113,6 +1081,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->state.drain) {
select_res |= CURL_CSELECT_IN;
DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ select_res |= CURL_CSELECT_OUT;
}
#endif
@@ -1155,7 +1125,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
#endif
- k->now = Curl_now();
+ now = Curl_now();
if(!didwhat) {
/* no read no write, this is a timeout? */
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
@@ -1172,7 +1142,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
*/
- timediff_t ms = Curl_timediff(k->now, k->start100);
+ timediff_t ms = Curl_timediff(now, k->start100);
if(ms >= data->set.expect_100_timeout) {
/* we've waited long enough, continue anyway */
k->exp100 = EXP100_SEND_DATA;
@@ -1182,35 +1152,31 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
}
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- result = Curl_quic_idle(data);
- if(result)
- goto out;
- }
-#endif
+ result = Curl_conn_ev_data_idle(data);
+ if(result)
+ goto out;
}
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, k->now);
+ result = Curl_speedcheck(data, now);
if(result)
goto out;
if(k->keepon) {
- if(0 > Curl_timeleft(data, &k->now, FALSE)) {
+ if(0 > Curl_timeleft(data, &now, FALSE)) {
if(k->size != -1) {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
- Curl_timediff(k->now, data->progress.t_startsingle),
+ Curl_timediff(now, data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
- Curl_timediff(k->now, data->progress.t_startsingle),
+ Curl_timediff(now, data->progress.t_startsingle),
k->bytecount);
}
result = CURLE_OPERATION_TIMEDOUT;
@@ -1264,7 +1230,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
result = CURLE_OK;
out:
- DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result));
+ if(result)
+ DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result));
return result;
}
@@ -1377,6 +1344,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
Curl_safefree(data->info.wouldredirect);
+ Curl_data_priority_clear_state(data);
if(data->state.httpreq == HTTPREQ_PUT)
data->state.infilesize = data->set.filesize;
@@ -1389,15 +1357,16 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
else
data->state.infilesize = 0;
-#ifndef CURL_DISABLE_COOKIES
/* If there is a list of cookie files to read, do it now! */
- if(data->state.cookielist)
- Curl_cookie_loadfiles(data);
-#endif
+ Curl_cookie_loadfiles(data);
+
/* If there is a list of host pairs to deal with */
if(data->state.resolve)
result = Curl_loadhostpairs(data);
+ /* If there is a list of hsts files to read */
+ Curl_hsts_loadfiles(data);
+
if(!result) {
/* Allow data->set.use_port to set which port to use. This needs to be
* disabled for example when we follow Location: headers to URLs using
@@ -1433,7 +1402,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
#endif
- Curl_http2_init_state(&data->state);
result = Curl_hsts_loadcb(data, data->hsts);
}
@@ -1871,7 +1839,7 @@ Curl_setup_transfer(
httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
(http->sending == HTTPSEND_REQUEST));
- if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) {
+ if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) {
/* when multiplexing, the read/write sockets need to be the same! */
conn->sockfd = sockindex == -1 ?
((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h
index 4092508729..536ac249b7 100644
--- a/Utilities/cmcurl/lib/transfer.h
+++ b/Utilities/cmcurl/lib/transfer.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 3ab63a068f..1bb93df913 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -431,10 +431,17 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_dyn_free(&data->state.headerb);
Curl_safefree(data->state.ulbuf);
Curl_flush_cookies(data, TRUE);
+#ifndef CURL_DISABLE_COOKIES
+ curl_slist_free_all(data->set.cookielist); /* clean up list */
+#endif
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);
Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
- Curl_hsts_cleanup(&data->hsts);
+#ifndef CURL_DISABLE_HSTS
+ if(!data->share || !data->share->hsts)
+ Curl_hsts_cleanup(&data->hsts);
+ curl_slist_free_all(data->set.hstslist); /* clean up list */
+#endif
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
@@ -445,7 +452,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_resolver_cancel(data);
Curl_resolver_cleanup(data->state.async.resolver);
- Curl_http2_cleanup_dependencies(data);
+ Curl_data_priority_cleanup(data);
/* No longer a dirty share, if it exists */
if(data->share) {
@@ -531,11 +538,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
/* Timeout every 24 hours by default */
set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
- set->proxyport = 0;
- set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
set->httpauth = CURLAUTH_BASIC; /* defaults to basic */
#ifndef CURL_DISABLE_PROXY
+ set->proxyport = 0;
+ set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
/* SOCKS5 proxy auth defaults to username/password + GSS-API */
set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
@@ -556,11 +563,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
#endif
set->ssl.primary.verifypeer = TRUE;
set->ssl.primary.verifyhost = TRUE;
-#ifdef USE_TLS_SRP
- set->ssl.primary.authtype = CURL_TLSAUTH_NONE;
-#endif
- /* defaults to any auth type */
+#ifdef USE_SSH
+ /* defaults to any auth type */
set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
+ set->new_directory_perms = 0755; /* Default permissions */
+#endif
set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by
default */
#ifndef CURL_DISABLE_PROXY
@@ -568,7 +575,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
#endif
set->new_file_perms = 0644; /* Default permissions */
- set->new_directory_perms = 0755; /* Default permissions */
set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP |
CURLPROTO_FTPS;
@@ -631,14 +637,15 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->maxage_conn = 118;
set->maxlifetime_conn = 0;
set->http09_allowed = FALSE;
- set->httpwant =
#ifdef USE_HTTP2
- CURL_HTTP_VERSION_2TLS
+ set->httpwant = CURL_HTTP_VERSION_2TLS
#else
- CURL_HTTP_VERSION_1_1
+ set->httpwant = CURL_HTTP_VERSION_1_1
#endif
;
- Curl_http2_init_userset(set);
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ memset(&set->priority, 0, sizeof(set->priority));
+#endif
set->quick_exit = 0L;
return result;
}
@@ -698,45 +705,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
return result;
}
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-static void conn_reset_postponed_data(struct connectdata *conn, int num)
-{
- struct postponed_data * const psnd = &(conn->postponed[num]);
- if(psnd->buffer) {
- DEBUGASSERT(psnd->allocated_size > 0);
- DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
- DEBUGASSERT(psnd->recv_size ?
- (psnd->recv_processed < psnd->recv_size) :
- (psnd->recv_processed == 0));
- DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD);
- free(psnd->buffer);
- psnd->buffer = NULL;
- psnd->allocated_size = 0;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
- }
- else {
- DEBUGASSERT(psnd->allocated_size == 0);
- DEBUGASSERT(psnd->recv_size == 0);
- DEBUGASSERT(psnd->recv_processed == 0);
- DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD);
- }
-}
-
-static void conn_reset_all_postponed_data(struct connectdata *conn)
-{
- conn_reset_postponed_data(conn, 0);
- conn_reset_postponed_data(conn, 1);
-}
-#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macro instead of function when workaround not used */
-#define conn_reset_all_postponed_data(c) do {} while(0)
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
-
static void conn_shutdown(struct Curl_easy *data)
{
DEBUGASSERT(data);
@@ -777,13 +745,14 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->sasl_authzid);
Curl_safefree(conn->options);
Curl_safefree(conn->oauth_bearer);
+#ifndef CURL_DISABLE_HTTP
Curl_dyn_free(&conn->trailer);
+#endif
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
Curl_safefree(conn->hostname_resolve);
Curl_safefree(conn->secondaryhostname);
- conn_reset_all_postponed_data(conn);
Curl_llist_destroy(&conn->easyq, NULL);
Curl_safefree(conn->localdev);
Curl_free_primary_ssl_config(&conn->ssl_config);
@@ -867,24 +836,6 @@ void Curl_disconnect(struct Curl_easy *data,
}
/*
- * This function should return TRUE if the socket is to be assumed to
- * be dead. Most commonly this happens when the server has closed the
- * connection due to inactivity.
- */
-static bool SocketIsDead(curl_socket_t sock)
-{
- int sval;
- bool ret_val = TRUE;
-
- sval = SOCKET_READABLE(sock, 0);
- if(sval == 0)
- /* timeout */
- ret_val = FALSE;
-
- return ret_val;
-}
-
-/*
* IsMultiplexingPossible()
*
* Return a bitmask with the available multiplexing options for the given
@@ -1014,8 +965,7 @@ static bool extract_if_dead(struct connectdata *conn,
}
else {
- /* Use the general method for determining the death of a connection */
- dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
+ dead = !Curl_conn_is_alive(data, conn);
}
if(dead) {
@@ -1344,8 +1294,10 @@ ConnectionExists(struct Curl_easy *data,
/* If multiplexing isn't enabled on the h2 connection and h1 is
explicitly requested, handle it: */
if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
- (check->httpversion >= 20) &&
- (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ (((check->httpversion >= 20) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ || ((check->httpversion >= 30) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_3))))
continue;
if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) {
@@ -1467,9 +1419,8 @@ ConnectionExists(struct Curl_easy *data,
#ifdef USE_NGHTTP2
/* If multiplexed, make sure we don't go over concurrency limit */
if(check->bits.multiplex) {
- /* Multiplexed connections can only be HTTP/2 for now */
- struct http_conn *httpc = &check->proto.httpc;
- if(multiplexed >= httpc->settings.max_concurrent_streams) {
+ if(multiplexed >= Curl_conn_get_max_concurrent(data, check,
+ FIRSTSOCKET)) {
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
multiplexed);
continue;
@@ -1551,15 +1502,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
- conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
- conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connection_id = -1; /* no ID */
conn->port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown at this point */
-#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD)
- conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
- conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */
/* Default protocol-independent behavior doesn't support persistent
connections, so we set this to force-close. Protocols that support
@@ -2329,7 +2274,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
goto error;
}
- /* path will be "/", if no path was was found */
+ /* path will be "/", if no path was found */
if(strcmp("/", path)) {
is_unix_proxy = TRUE;
free(host);
@@ -2412,6 +2357,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
char *socksproxy = NULL;
char *no_proxy = NULL;
CURLcode result = CURLE_OK;
+ bool spacesep = FALSE;
/*************************************************************
* Extract the user and password from the authentication string
@@ -2458,7 +2404,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
}
if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
- data->set.str[STRING_NOPROXY] : no_proxy)) {
+ data->set.str[STRING_NOPROXY] : no_proxy,
+ &spacesep)) {
Curl_safefree(proxy);
Curl_safefree(socksproxy);
}
@@ -2467,6 +2414,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
/* if the host is not in the noproxy list, detect proxy. */
proxy = detect_proxy(data, conn);
#endif /* CURL_DISABLE_HTTP */
+ if(spacesep)
+ infof(data, "space-separated NOPROXY patterns are deprecated");
Curl_safefree(no_proxy);
@@ -2795,7 +2744,7 @@ static CURLcode override_login(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
/* no user was set but a password, set a blank user */
- if(userp && !*userp && *passwdp) {
+ if(!*userp && *passwdp) {
*userp = strdup("");
if(!*userp)
return CURLE_OUT_OF_MEMORY;
@@ -3345,12 +3294,6 @@ static void reuse_conn(struct Curl_easy *data,
struct connectdata *temp,
struct connectdata *existing)
{
- /* 'local_ip' and 'local_port' get filled with local's numerical
- ip address and port number whenever an outgoing connection is
- **established** from the primary socket to a remote address. */
- char local_ip[MAX_IPADR_LEN] = "";
- int local_port = -1;
-
/* get the user+password information from the temp struct since it may
* be new for this request even when we re-use an existing connection */
if(temp->user) {
@@ -3382,6 +3325,20 @@ static void reuse_conn(struct Curl_easy *data,
}
#endif
+ /* Finding a connection for reuse in the cache matches, among other
+ * things on the "remote-relevant" hostname. This is not necessarily
+ * the authority of the URL, e.g. conn->host. 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
+ * switch `existing` conn to `temp` conn's host settings.
+ * TODO: is this correct in the case of TLS connections that have
+ * used the original hostname in SNI to negotiate? Do we send
+ * requests for another host through the different SNI?
+ */
Curl_free_idnconverted_hostname(&existing->host);
Curl_free_idnconverted_hostname(&existing->conn_to_host);
Curl_safefree(existing->host.rawalloc);
@@ -3398,15 +3355,6 @@ static void reuse_conn(struct Curl_easy *data,
existing->hostname_resolve = temp->hostname_resolve;
temp->hostname_resolve = NULL;
- /* persist connection info in session handle */
- if(existing->transport == TRNSPRT_TCP) {
- Curl_conninfo_local(data, existing->sock[FIRSTSOCKET],
- local_ip, &local_port);
- }
- Curl_persistconninfo(data, existing, local_ip, local_port);
-
- conn_reset_all_postponed_data(temp); /* free buffers */
-
/* re-use init */
existing->bits.reuse = TRUE; /* yes, we're re-using here */
@@ -3880,6 +3828,13 @@ static CURLcode create_conn(struct Curl_easy *data,
* Resolve the address of the server or proxy
*************************************************************/
result = resolve_server(data, conn, async);
+ if(result)
+ goto out;
+
+ /* Everything general done, inform filters that they need
+ * to prepare for a data transfer.
+ */
+ result = Curl_conn_ev_data_setup(data);
out:
return result;
@@ -4007,7 +3962,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
data->state.httpreq = HTTPREQ_HEAD;
k->start = Curl_now(); /* start time */
- k->now = k->start; /* current time is now */
k->header = TRUE; /* assume header */
k->bytecount = 0;
k->ignorebody = FALSE;
@@ -4018,3 +3972,103 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
return CURLE_OK;
}
+
+#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;
+ 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 = 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 */
+
+void Curl_data_priority_cleanup(struct Curl_easy *data)
+{
+#ifdef USE_NGHTTP2
+ 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)data;
+}
+
+void Curl_data_priority_clear_state(struct Curl_easy *data)
+{
+ memset(&data->state.priority, 0, sizeof(data->state.priority));
+}
+
+#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h
index 1a03c564c6..3b58df403e 100644
--- a/Utilities/cmcurl/lib/url.h
+++ b/Utilities/cmcurl/lib/url.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,4 +59,20 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
#endif
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+void Curl_data_priority_cleanup(struct Curl_easy *data);
+void Curl_data_priority_clear_state(struct Curl_easy *data);
+#else
+#define Curl_data_priority_cleanup(x)
+#define Curl_data_priority_clear_state(x)
+#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */
+
+#ifdef USE_NGHTTP2
+CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
+ struct Curl_easy *child,
+ bool exclusive);
+#else
+#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN
+#endif
+
#endif /* HEADER_CURL_URL_H */
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h
index 43a83ef6e4..28e5dd7ea4 100644
--- a/Utilities/cmcurl/lib/urlapi-int.h
+++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index b96af35ad2..29927b3d92 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,6 +33,7 @@
#include "inet_pton.h"
#include "inet_ntop.h"
#include "strdup.h"
+#include "idn.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -116,14 +117,11 @@ static const char *find_host_sep(const char *url)
}
/*
- * Decide in an encoding-independent manner whether a character in a URL must
- * be escaped. This is used in urlencode_str().
+ * Decide whether a character in a URL must be escaped.
*/
-static bool urlchar_needs_escaping(int c)
-{
- return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
-}
+#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)))
+static const char hexdigits[] = "0123456789abcdef";
/* urlencode_str() writes data into an output dynbuf and URL-encodes the
* spaces in the source URL accordingly.
*
@@ -167,7 +165,10 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
left = FALSE;
if(urlchar_needs_escaping(*iptr)) {
- if(Curl_dyn_addf(o, "%%%02x", *iptr))
+ char out[3]={'%'};
+ out[1] = hexdigits[*iptr>>4];
+ out[2] = hexdigits[*iptr & 0xf];
+ if(Curl_dyn_addn(o, out, 3))
return CURLUE_OUT_OF_MEMORY;
}
else {
@@ -492,35 +493,21 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
bool has_scheme)
{
- char *portptr = NULL;
- char endbracket;
- int len;
+ char *portptr;
char *hostname = Curl_dyn_ptr(host);
/*
* Find the end of an IPv6 address, either on the ']' ending bracket or
* a percent-encoded zone index.
*/
- if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
- &endbracket, &len)) {
- if(']' == endbracket)
- portptr = &hostname[len];
- else if('%' == endbracket) {
- int zonelen = len;
- if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
- if(']' != endbracket)
- return CURLUE_BAD_IPV6;
- portptr = &hostname[--zonelen + len + 1];
- }
- else
- return CURLUE_BAD_IPV6;
- }
- else
+ if(hostname[0] == '[') {
+ portptr = strchr(hostname, ']');
+ if(!portptr)
return CURLUE_BAD_IPV6;
-
+ portptr++;
/* this is a RFC2732-style specified IP-address */
- if(portptr && *portptr) {
+ if(*portptr) {
if(*portptr != ':')
- return CURLUE_BAD_IPV6;
+ return CURLUE_BAD_PORT_NUMBER;
}
else
portptr = NULL;
@@ -584,11 +571,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
hostname++;
hlen -= 2;
- if(hostname[hlen] != ']')
- return CURLUE_BAD_IPV6;
-
- /* only valid letters are ok */
+ /* only valid IPv6 letters are ok */
len = strspn(hostname, l);
+
if(hlen != len) {
hlen = len;
if(hostname[len] == '%') {
@@ -602,8 +587,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
while(*h && (*h != ']') && (i < 15))
zoneid[i++] = *h++;
if(!i || (']' != *h))
- /* impossible to reach? */
- return CURLUE_MALFORMED_INPUT;
+ return CURLUE_BAD_IPV6;
zoneid[i] = 0;
u->zoneid = strdup(zoneid);
if(!u->zoneid)
@@ -782,25 +766,28 @@ static CURLUcode decode_host(struct dynbuf *host)
*
* RETURNS
*
- * an allocated dedotdotified output string
+ * Zero for success and 'out' set to an allocated dedotdotified string.
*/
-UNITTEST char *dedotdotify(const char *input, size_t clen);
-UNITTEST char *dedotdotify(const char *input, size_t clen)
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
{
- char *out = malloc(clen + 1);
char *outptr;
const char *orginput = input;
char *queryp;
+ char *out;
+
+ *outp = NULL;
+ /* the path always starts with a slash, and a slash has not dot */
+ if((clen < 2) || !memchr(input, '.', clen))
+ return 0;
+
+ out = malloc(clen + 1);
if(!out)
- return NULL; /* out of memory */
+ return 1; /* out of memory */
*out = 0; /* null-terminates, for inputs like "./" */
outptr = out;
- if(!*input)
- /* zero length input string, return that */
- return out;
-
/*
* To handle query-parts properly, we must find it and remove it during the
* dotdot-operation and then append it again at the end to the output
@@ -905,7 +892,8 @@ UNITTEST char *dedotdotify(const char *input, size_t clen)
memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */
}
- return out;
+ *outp = out;
+ return 0; /* success */
}
static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
@@ -1152,7 +1140,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
size_t qlen = strlen(query) - fraglen; /* includes '?' */
pathlen = strlen(path) - qlen - fraglen;
if(qlen > 1) {
- if(qlen && (flags & CURLU_URLENCODE)) {
+ if(flags & CURLU_URLENCODE) {
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
/* skip the leading question mark */
@@ -1199,8 +1187,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
path = u->path = Curl_dyn_ptr(&enc);
}
- if(!pathlen) {
- /* there is no path left, unset */
+ if(pathlen <= 1) {
+ /* there is no path left or just the slash, unset */
path = NULL;
}
else {
@@ -1224,13 +1212,16 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(!(flags & CURLU_PATH_AS_IS)) {
/* remove ../ and ./ sequences according to RFC3986 */
- char *newp = dedotdotify((char *)path, pathlen);
- if(!newp) {
+ char *dedot;
+ int err = dedotdotify((char *)path, pathlen, &dedot);
+ if(err) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
}
- free(u->path);
- u->path = newp;
+ if(dedot) {
+ free(u->path);
+ u->path = dedot;
+ }
}
}
@@ -1379,6 +1370,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
char portbuf[7];
bool urldecode = (flags & CURLU_URLDECODE)?1:0;
bool urlencode = (flags & CURLU_URLENCODE)?1:0;
+ bool punycode = FALSE;
bool plusdecode = FALSE;
(void)flags;
if(!u)
@@ -1408,6 +1400,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
case CURLUPART_HOST:
ptr = u->host;
ifmissing = CURLUE_NO_HOST;
+ punycode = (flags & CURLU_PUNYCODE)?1:0;
break;
case CURLUPART_ZONEID:
ptr = u->zoneid;
@@ -1460,6 +1453,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
char *options = u->options;
char *port = u->port;
char *allochost = NULL;
+ punycode = (flags & CURLU_PUNYCODE)?1:0;
if(u->scheme && strcasecompare("file", u->scheme)) {
url = aprintf("file://%s%s%s",
u->path,
@@ -1514,6 +1508,17 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
if(!allochost)
return CURLUE_OUT_OF_MEMORY;
}
+ else if(punycode) {
+ if(!Curl_is_ASCII_name(u->host)) {
+#ifndef USE_IDN
+ return CURLUE_LACKS_IDN;
+#else
+ allochost = Curl_idn_decode(u->host);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+#endif
+ }
+ }
else {
/* only encode '%' in output host name */
char *host = u->host;
@@ -1611,6 +1616,19 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
free(*part);
*part = Curl_dyn_ptr(&enc);
}
+ else if(punycode) {
+ if(!Curl_is_ASCII_name(u->host)) {
+#ifndef USE_IDN
+ return CURLUE_LACKS_IDN;
+#else
+ char *allochost = Curl_idn_decode(*part);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+ free(*part);
+ *part = allochost;
+#endif
+ }
+ }
return CURLUE_OK;
}
@@ -1807,7 +1825,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
return CURLUE_OUT_OF_MEMORY;
}
else {
- result = Curl_dyn_addf(&enc, "%%%02x", *i);
+ char out[3]={'%'};
+ out[1] = hexdigits[*i>>4];
+ out[2] = hexdigits[*i & 0xf];
+ result = Curl_dyn_addn(&enc, out, 3);
if(result)
return CURLUE_OUT_OF_MEMORY;
}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index 3d7545c689..4cfffa7737 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -113,23 +113,6 @@ typedef unsigned int curl_prot_t;
input easier and better. */
#define CURL_MAX_INPUT_LENGTH 8000000
-/* Macros intended for DEBUGF logging, use like:
- * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
- * and it will output:
- * [CONN-1-0][CF-SSL] this filter very much rocks
- * on connection #1 with sockindex 0 for filter of type "SSL". */
-#define DMSG(d,msg) \
- "[CONN-%ld] "msg, (d)->conn->connection_id
-#define DMSGI(d,i,msg) \
- "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
-#define CMSG(c,msg) \
- "[CONN-%ld] "msg, (conn)->connection_id
-#define CMSGI(c,i,msg) \
- "[CONN-%ld-%d] "msg, (conn)->connection_id, (i)
-#define CFMSG(cf,msg) \
- "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
- (cf)->sockindex, (cf)->cft->name
-
#include "cookie.h"
#include "psl.h"
@@ -187,8 +170,8 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
#include "mqtt.h"
#include "wildcard.h"
#include "multihandle.h"
-#include "quic.h"
#include "c-hyper.h"
+#include "cf-socket.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@@ -268,8 +251,6 @@ typedef enum {
struct ssl_backend_data;
struct ssl_primary_config {
- long version; /* what version the client wants to use */
- long version_max; /* max supported version the client wants to use */
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* certificate to verify peer against */
char *issuercert; /* optional issuer certificate filename */
@@ -284,10 +265,11 @@ struct ssl_primary_config {
#ifdef USE_TLS_SRP
char *username; /* TLS username (for, e.g., SRP) */
char *password; /* TLS password (for, e.g., SRP) */
- enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */
#endif
char *curves; /* list of curves to use */
unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */
+ unsigned int version_max; /* max supported version the client wants to use */
+ unsigned char 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 */
@@ -648,7 +630,6 @@ struct SingleRequest {
curl_off_t pendingheader; /* this many bytes left to send is actually
header and not body */
struct curltime start; /* transfer started at this time */
- struct curltime now; /* current time */
enum {
HEADER_NORMAL, /* no bad header at all */
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
@@ -707,6 +688,7 @@ struct SingleRequest {
struct dohdata *doh; /* DoH specific data for this request */
#endif
unsigned char setcookies;
+ unsigned char writer_stack_depth; /* Unencoding stack depth. */
BIT(header); /* incoming data has HTTP header */
BIT(content_range); /* set TRUE if Content-Range: was found */
BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
@@ -783,8 +765,8 @@ struct Curl_handler {
/* This function *MAY* be set to a protocol-dependent function that is run
* by the curl_disconnect(), as a step in the disconnection. If the handler
* is called because the connection has been considered dead,
- * dead_connection is set to TRUE. The connection is already disassociated
- * from the transfer here.
+ * dead_connection is set to TRUE. The connection is (again) associated with
+ * the transfer here.
*/
CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
bool dead_connection);
@@ -830,7 +812,7 @@ struct Curl_handler {
#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per
request instead of per connection */
#define PROTOPT_ALPN (1<<8) /* set ALPN for this */
-#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */
+/* (1<<9) was PROTOPT_STREAM, now free */
#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field
of the URL */
#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a
@@ -848,20 +830,6 @@ struct Curl_handler {
#define CONNRESULT_NONE 0 /* No extra information. */
#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-struct postponed_data {
- char *buffer; /* Temporal store for received data during
- sending, must be freed */
- size_t allocated_size; /* Size of temporal store */
- size_t recv_size; /* Size of received data during sending */
- size_t recv_processed; /* Size of processed part of postponed data */
-#ifdef DEBUGBUILD
- curl_socket_t bindsock;/* Structure must be bound to specific socket,
- used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
-};
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
-
struct proxy_info {
struct hostname host;
int port;
@@ -909,16 +877,9 @@ struct connectdata {
there is no name resolve done. */
struct Curl_dns_entry *dns_entry;
- /* 'ip_addr' is the particular IP we connected to. It points to a struct
- within the DNS cache, so this pointer is only valid as long as the DNS
- cache entry remains locked. It gets unlocked in multi_done() */
- struct Curl_addrinfo *ip_addr;
- struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */
-
-#ifdef ENABLE_QUIC
- struct quicsocket hequic[2]; /* two, for happy eyeballs! */
- struct quicsocket *quic;
-#endif
+ /* 'remote_addr' is the particular IP we connected to. it is owned, set
+ * and NULLed by the connected socket filter (if there is one). */
+ const struct Curl_sockaddr_ex *remote_addr;
struct hostname host;
char *hostname_resolve; /* host name to resolve to address, allocated */
@@ -947,31 +908,16 @@ struct connectdata {
struct curltime lastused; /* when returned to the connection cache */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
transfer when doing FTP */
- curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
- int tempfamily[2]; /* family used for the temp sockets */
Curl_recv *recv[2];
Curl_send *send[2];
struct Curl_cfilter *cfilter[2]; /* connection filters */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
- struct postponed_data postponed[2]; /* two buffers for two sockets */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
struct ssl_primary_config ssl_config;
#ifndef CURL_DISABLE_PROXY
struct ssl_primary_config proxy_ssl_config;
#endif
struct ConnectBits bits; /* various state-flags for this connection */
- /* connecttime: when connect() is called on the current IP address. Used to
- be able to track when to move on to try next IP - but only when the multi
- interface is used. */
- struct curltime connecttime;
-
- /* The field below gets set in Curl_connecthost */
- /* how long time in milliseconds to spend on trying to connect to each IP
- address, per family */
- timediff_t timeoutms_per_addr[2];
-
const struct Curl_handler *handler; /* Connection's protocol handler */
const struct Curl_handler *given; /* The protocol first given */
@@ -1034,16 +980,15 @@ struct connectdata {
struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
#endif
+#ifndef CURL_DISABLE_HTTP
/* for chunked-encoded trailer */
struct dynbuf trailer;
+#endif
union {
#ifndef CURL_DISABLE_FTP
struct ftp_conn ftpc;
#endif
-#ifndef CURL_DISABLE_HTTP
- struct http_conn httpc;
-#endif
#ifdef USE_SSH
struct ssh_conn sshc;
#endif
@@ -1070,6 +1015,9 @@ struct connectdata {
#ifndef CURL_DISABLE_MQTT
struct mqtt_conn mqtt;
#endif
+#ifdef USE_WEBSOCKETS
+ struct ws_conn ws;
+#endif
} proto;
struct connectbundle *bundle; /* The bundle we are member of */
@@ -1086,14 +1034,13 @@ struct connectdata {
that subsequent bound-requested connections aren't accidentally re-using
wrong connections. */
char *localdev;
- int localportrange;
+ unsigned short localportrange;
int cselect_bits; /* bitmask of socket events */
int waitfor; /* current READ/WRITE bits to wait for */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
int socks5_gssapi_enctype;
#endif
- /* The field below gets set in Curl_connecthost */
- int num_addr; /* number of addresses to try to connect to */
+ /* The field below gets set in connect.c:connecthost() */
int port; /* which port to use locally - to connect to */
int remote_port; /* the remote port, not the proxy port! */
int conn_to_port; /* the remote port to connect to. valid only if
@@ -1238,10 +1185,29 @@ struct auth {
should be RFC compliant */
};
-struct Curl_http2_dep {
- struct Curl_http2_dep *next;
+#ifdef USE_NGHTTP2
+struct Curl_data_prio_node {
+ struct Curl_data_prio_node *next;
struct Curl_easy *data;
};
+#endif
+
+/**
+ * Priority information for an easy handle in relation to others
+ * on the same connection.
+ * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218
+ */
+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
+};
/*
* This struct is for holding data that was attempted to get sent to the user's
@@ -1270,6 +1236,7 @@ typedef enum {
EXPIRE_TOOFAST,
EXPIRE_QUIC,
EXPIRE_FTP_ACCEPT,
+ EXPIRE_ALPN_EYEBALLS,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
@@ -1389,24 +1356,17 @@ struct UrlState {
size_t drain; /* Increased when this stream has data to read, even if its
socket is not necessarily is readable. Decreased when
checked. */
+ struct Curl_data_priority priority; /* shallow copy of data->set */
#endif
curl_read_callback fread_func; /* read callback/function */
void *in; /* CURLOPT_READDATA */
-#ifdef USE_HTTP2
- struct Curl_easy *stream_depends_on;
- int stream_weight;
-#endif
CURLU *uh; /* URL handle for the current parsed URL */
struct urlpieces up;
unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
is this */
char *url; /* work URL, copied from UserDefined */
char *referer; /* referer string */
-#ifndef CURL_DISABLE_COOKIES
- struct curl_slist *cookielist; /* list of cookie files set by
- curl_easy_setopt(COOKIEFILE) calls */
-#endif
struct curl_slist *resolve; /* set to point to the set.resolve list when
this should be dealt with in pretransfer */
#ifndef CURL_DISABLE_HTTP
@@ -1471,7 +1431,6 @@ struct UrlState {
BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE
when multi_done() is called, to prevent multi_done() to get
invoked twice when the multi interface is used. */
- BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
BIT(cookie_engine);
BIT(prefer_ascii); /* ASCII rather than binary */
@@ -1620,15 +1579,9 @@ struct UserDefined {
void *out; /* CURLOPT_WRITEDATA */
void *in_set; /* CURLOPT_READDATA */
void *writeheader; /* write the header to this if non-NULL */
- unsigned short proxyport; /* If non-zero, use this port number by
- default. If the proxy string features a
- ":[port]" that one will override this. */
unsigned short use_port; /* which port to use (when not using default) */
unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */
unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */
-#ifndef CURL_DISABLE_PROXY
- unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
-#endif
long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1
for infinity */
@@ -1638,8 +1591,9 @@ struct UserDefined {
of strlen(), and then the data *may* be binary
(contain zero bytes) */
unsigned short localport; /* local port number to bind to */
- int localportrange; /* number of additional port numbers to test in case the
- 'localport' one can't be bind()ed */
+ unsigned short localportrange; /* number of additional port numbers to test
+ in case the 'localport' one can't be
+ bind()ed */
curl_write_callback fwrite_func; /* function that stores the output */
curl_write_callback fwrite_header; /* function that stores headers */
curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */
@@ -1661,7 +1615,13 @@ struct UserDefined {
void *prereq_userp; /* pre-initial request user data */
void *seek_client; /* pointer to pass to the seek callback */
+#ifndef CURL_DISABLE_COOKIES
+ struct curl_slist *cookielist; /* list of cookie files set by
+ curl_easy_setopt(COOKIEFILE) calls */
+#endif
#ifndef CURL_DISABLE_HSTS
+ struct curl_slist *hstslist; /* list of HSTS files set by
+ curl_easy_setopt(HSTS) calls */
curl_hstsread_callback hsts_read;
void *hsts_read_userp;
curl_hstswrite_callback hsts_write;
@@ -1688,17 +1648,8 @@ struct UserDefined {
download */
curl_off_t set_resume_from; /* continue [ftp] transfer from here */
struct curl_slist *headers; /* linked list of extra headers */
- struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
struct curl_httppost *httppost; /* linked list of old POST data */
curl_mimepart mimepost; /* MIME/POST data. */
- struct curl_slist *quote; /* after connection is established */
- struct curl_slist *postquote; /* after the transfer */
- struct curl_slist *prequote; /* before the transfer, after type */
- struct curl_slist *source_quote; /* 3rd party quote */
- struct curl_slist *source_prequote; /* in 3rd party transfer mode - before
- the transfer on source host */
- struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
- the transfer on source host */
#ifndef CURL_DISABLE_TELNET
struct curl_slist *telnet_options; /* linked list of telnet options */
#endif
@@ -1708,13 +1659,18 @@ struct UserDefined {
the hostname and port to connect to */
time_t timevalue; /* what time to compare with */
unsigned char timecondition; /* kind of time comparison: curl_TimeCond */
- unsigned char proxytype; /* what kind of proxy: curl_proxytype */
unsigned char method; /* what kind of HTTP request: Curl_HttpReq */
unsigned char httpwant; /* when non-zero, a specific HTTP version requested
to be used in the library's request(s) */
struct ssl_config_data ssl; /* user defined SSL stuff */
#ifndef CURL_DISABLE_PROXY
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
+ struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
+ unsigned short proxyport; /* If non-zero, use this port number by
+ default. If the proxy string features a
+ ":[port]" that one will override this. */
+ unsigned char proxytype; /* what kind of proxy: curl_proxytype */
+ unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
#endif
struct ssl_general_config general_ssl; /* general user defined SSL stuff */
int dns_cache_timeout; /* DNS cache timeout (seconds) */
@@ -1722,7 +1678,9 @@ struct UserDefined {
unsigned int upload_buffer_size; /* size of upload buffer to use,
keep it >= CURL_MAX_WRITE_SIZE */
void *private_data; /* application-private data */
+#ifndef CURL_DISABLE_HTTP
struct curl_slist *http200aliases; /* linked list of aliases for http200 */
+#endif
unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
file 0 - whatever, 1 - v2, 2 - v6 */
curl_off_t max_filesize; /* Maximum file size to download */
@@ -1732,26 +1690,32 @@ struct UserDefined {
unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */
unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */
#endif
- /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
+ struct curl_slist *quote; /* after connection is established */
+ struct curl_slist *postquote; /* after the transfer */
+ struct curl_slist *prequote; /* before the transfer, after type */
+ /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP
1 - create directories that don't exist
2 - the same but also allow MKD to fail once
*/
unsigned char ftp_create_missing_dirs;
+#endif
#ifdef USE_LIBSSH2
curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
void *ssh_hostkeyfunc_userp; /* custom pointer to callback */
#endif
-
+#ifdef USE_SSH
curl_sshkeycallback ssh_keyfunc; /* key matching callback */
void *ssh_keyfunc_userp; /* custom pointer to callback */
+ int ssh_auth_types; /* allowed SSH auth types */
+ unsigned int new_directory_perms; /* when creating remote dirs */
+#endif
#ifndef CURL_DISABLE_NETRC
unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */
#endif
curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
IMAP or POP3 or others! */
unsigned int new_file_perms; /* when creating remote files */
- unsigned int new_directory_perms; /* when creating remote dirs */
- int ssh_auth_types; /* allowed SSH auth types */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
struct curl_blob *blobs[BLOB_LAST];
#ifdef ENABLE_IPV6
@@ -1759,8 +1723,9 @@ struct UserDefined {
#endif
curl_prot_t allowed_protocols;
curl_prot_t redir_protocols;
+#ifndef CURL_DISABLE_MIME
unsigned int mime_options; /* Mime option flags. */
-
+#endif
#ifndef CURL_DISABLE_RTSP
void *rtp_out; /* write RTP to this if non-NULL */
/* Common RTSP header options */
@@ -1775,8 +1740,9 @@ struct UserDefined {
to pattern (e.g. if WILDCARDMATCH is on) */
void *fnmatch_data;
#endif
- long gssapi_delegation; /* GSS-API credential delegation, see the
- documentation of CURLOPT_GSSAPI_DELEGATION */
+ /* GSS-API credential delegation, see the documentation of
+ CURLOPT_GSSAPI_DELEGATION */
+ unsigned char gssapi_delegation;
int tcp_keepidle; /* seconds in idle before sending keepalive probe */
int tcp_keepintvl; /* seconds between TCP keepalive probes */
@@ -1784,10 +1750,8 @@ struct UserDefined {
size_t maxconnects; /* Max idle connections in the connection cache */
long expect_100_timeout; /* in milliseconds */
-#ifdef USE_HTTP2
- struct Curl_easy *stream_depends_on;
- int stream_weight;
- struct Curl_http2_dep *stream_dependents;
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ struct Curl_data_priority priority;
#endif
curl_resolver_start_callback resolver_start; /* optional callback called
before resolver start */
@@ -1798,8 +1762,10 @@ struct UserDefined {
struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
#endif
CURLU *uh; /* URL handle for the current parsed URL */
+#ifndef CURL_DISABLE_HTTP
void *trailer_data; /* pointer to pass to trailer data callback */
curl_trailer_callback trailer_callback; /* trailing data callback */
+#endif
char keep_post; /* keep POSTs as POSTs after a 30x request; each
bit represents a request, from 301 to 303 */
#ifndef CURL_DISABLE_SMTP
@@ -1877,7 +1843,6 @@ struct UserDefined {
BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers
from user callbacks */
BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */
- BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1
header */
BIT(abstract_unix_socket);
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c
index b82b171467..c651fc5145 100644
--- a/Utilities/cmcurl/lib/vauth/cleartext.c
+++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,7 +28,8 @@
#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 <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c
index 475d31b8d7..5894ed4bcf 100644
--- a/Utilities/cmcurl/lib/vauth/cram.c
+++ b/Utilities/cmcurl/lib/vauth/cram.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index c81ce10958..b7a0d92a31 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h
index d785bdd91b..68fdb28c47 100644
--- a/Utilities/cmcurl/lib/vauth/digest.h
+++ b/Utilities/cmcurl/lib/vauth/digest.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 6c95a3ef70..8fb8669393 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/gsasl.c b/Utilities/cmcurl/lib/vauth/gsasl.c
index a73c644434..c7d0a8d3b2 100644
--- a/Utilities/cmcurl/lib/vauth/gsasl.c
+++ b/Utilities/cmcurl/lib/vauth/gsasl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, et al.
+ * Copyright (C) Simon Josefsson, <simon@josefsson.org>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index bac78049dc..65eb3e1b50 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index 015bc66a35..c487149b9d 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index 0141e173fd..2a5d4a4908 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.h b/Utilities/cmcurl/lib/vauth/ntlm.h
index 14ebba2440..31ce921cd1 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.h
+++ b/Utilities/cmcurl/lib/vauth/ntlm.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index 193576acaa..5118963f4d 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c
index 1604b303e0..a4adbdcf15 100644
--- a/Utilities/cmcurl/lib/vauth/oauth2.c
+++ b/Utilities/cmcurl/lib/vauth/oauth2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,8 @@
#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 <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
index 25dff967d1..e1d52b755c 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
index d845caca6c..d3245d0b18 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c
index 58fe05139d..62fc7c40fe 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.c
+++ b/Utilities/cmcurl/lib/vauth/vauth.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index c310c66375..e17d7aad6e 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index c80841c166..55b54a06e8 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,12 +24,16 @@
#include "curl_setup.h"
+#ifdef USE_NGHTTP2
+#include <nghttp2/nghttp2.h>
+#endif
+
#include <curl/curl.h>
#include "urldata.h"
#include "vtls/vtls.h"
#include "http2.h"
#include "vssh/ssh.h"
-#include "quic.h"
+#include "vquic/vquic.h"
#include "curl_printf.h"
#include "easy_lock.h"
diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c
index e8f14f9dff..872d5b4f3c 100644
--- a/Utilities/cmcurl/lib/version_win32.c
+++ b/Utilities/cmcurl/lib/version_win32.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h
index 7a9a6a14f1..3899174a31 100644
--- a/Utilities/cmcurl/lib/version_win32.h
+++ b/Utilities/cmcurl/lib/version_win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c
new file mode 100644
index 0000000000..1930703418
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -0,0 +1,841 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_MSH3
+
+#include "urldata.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "curl_log.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "h2h3.h"
+#include "curl_msh3.h"
+#include "socketpair.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DEBUG_CF 1
+
+#if DEBUG_CF && defined(DEBUGBUILD)
+#define CF_DEBUGF(x) x
+#else
+#define CF_DEBUGF(x) do { } while(0)
+#endif
+
+#define MSH3_REQ_INIT_BUF_LEN 16384
+#define MSH3_REQ_MAX_BUF_LEN 0x100000
+
+#ifdef _WIN32
+#define msh3_lock CRITICAL_SECTION
+#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
+#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
+#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
+#define msh3_lock_release(lock) LeaveCriticalSection(lock)
+#else /* !_WIN32 */
+#include <pthread.h>
+#define msh3_lock pthread_mutex_t
+#define msh3_lock_initialize(lock) do { \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr); \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
+ pthread_mutex_init(lock, &attr); \
+ pthread_mutexattr_destroy(&attr); \
+}while(0)
+#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
+#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
+#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
+#endif /* _WIN32 */
+
+
+static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
+ void *IfContext);
+static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
+ void *IfContext);
+static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
+ void *IfContext,
+ MSH3_REQUEST *Request);
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header);
+static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t *Length,
+ const uint8_t *Data);
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError);
+static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
+ void *IfContext);
+static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
+ void *IfContext, void *SendContext);
+
+
+void Curl_msh3_ver(char *p, size_t len)
+{
+ uint32_t v[4];
+ MsH3Version(v);
+ (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
+}
+
+#define SP_LOCAL 0
+#define SP_REMOTE 1
+
+struct cf_msh3_ctx {
+ MSH3_API *api;
+ MSH3_CONNECTION *qconn;
+ struct Curl_sockaddr_ex addr;
+ curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
+ char l_ip[MAX_IPADR_LEN]; /* local IP as string */
+ int l_port; /* local port number */
+ struct curltime connect_started; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ /* Flags written by msh3/msquic thread */
+ bool handshake_complete;
+ bool handshake_succeeded;
+ bool connected;
+ /* Flags written by curl thread */
+ BIT(verbose);
+ BIT(active);
+};
+
+static const MSH3_CONNECTION_IF msh3_conn_if = {
+ msh3_conn_connected,
+ msh3_conn_shutdown_complete,
+ msh3_conn_new_request
+};
+
+static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
+ void *IfContext)
+{
+ struct cf_msh3_ctx *ctx = IfContext;
+ (void)Connection;
+ if(ctx->verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: connected\n"));
+ ctx->handshake_succeeded = true;
+ ctx->connected = true;
+ ctx->handshake_complete = true;
+}
+
+static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
+ void *IfContext)
+{
+ struct cf_msh3_ctx *ctx = IfContext;
+ (void)Connection;
+ if(ctx->verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: shutdown complete\n"));
+ ctx->connected = false;
+ ctx->handshake_complete = true;
+}
+
+static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
+ void *IfContext,
+ MSH3_REQUEST *Request)
+{
+ (void)Connection;
+ (void)IfContext;
+ (void)Request;
+}
+
+static const MSH3_REQUEST_IF msh3_request_if = {
+ msh3_header_received,
+ msh3_data_received,
+ msh3_complete,
+ msh3_shutdown_complete,
+ msh3_data_sent
+};
+
+static CURLcode msh3_data_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ (void)cf;
+
+ DEBUGASSERT(stream);
+ if(!stream->recv_buf) {
+ DEBUGF(LOG_CF(data, cf, "req: setup"));
+ stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
+ if(!stream->recv_buf) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ stream->req = ZERO_NULL;
+ msh3_lock_initialize(&stream->recv_lock);
+ stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
+ stream->recv_buf_max = MSH3_REQ_MAX_BUF_LEN;
+ stream->recv_header_len = 0;
+ stream->recv_header_complete = false;
+ stream->recv_data_len = 0;
+ stream->recv_data_complete = false;
+ stream->recv_error = CURLE_OK;
+ }
+ return CURLE_OK;
+}
+
+/* Requires stream->recv_lock to be held */
+static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
+{
+ uint8_t *new_recv_buf;
+ const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+
+ if(cur_recv_len + len > stream->recv_buf_alloc) {
+ size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
+ do {
+ new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
+ } while(cur_recv_len + len > new_recv_buf_alloc_len);
+ CF_DEBUGF(fprintf(stderr, "* enlarging buffer to %zu\n",
+ new_recv_buf_alloc_len));
+ new_recv_buf = malloc(new_recv_buf_alloc_len);
+ if(!new_recv_buf) {
+ CF_DEBUGF(fprintf(stderr, "* FAILED: enlarging buffer to %zu\n",
+ new_recv_buf_alloc_len));
+ return false;
+ }
+ if(cur_recv_len) {
+ memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
+ }
+ stream->recv_buf_alloc = new_recv_buf_alloc_len;
+ free(stream->recv_buf);
+ stream->recv_buf = new_recv_buf;
+ }
+ return true;
+}
+
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ size_t total_len;
+ (void)Request;
+
+ if(stream->recv_header_complete) {
+ CF_DEBUGF(fprintf(stderr, "* ignoring header after data\n"));
+ return;
+ }
+
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if((Header->NameLength == 7) &&
+ !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
+ total_len = 10 + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
+ (int)Header->NameLength, Header->Name));
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value);
+ }
+ else {
+ total_len = 4 + Header->NameLength + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
+ (int)Header->NameLength, Header->Name));
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "%.*s: %.*s\r\n",
+ (int)Header->NameLength, Header->Name,
+ (int)Header->ValueLength, Header->Value);
+ }
+
+ stream->recv_header_len += total_len;
+ data->state.drain = 1;
+
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t *Length,
+ const uint8_t *Data)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+
+ (void)Request;
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: received %u. %zu buffered, "
+ "%zu allocated\n",
+ *Length, cur_recv_len, stream->recv_buf_alloc));
+ /* TODO - Update this code to limit data bufferring by `stream->recv_buf_max`
+ and return `false` when we reach that limit. Then, when curl drains some
+ of the buffer, making room, call MsH3RequestSetReceiveEnabled to enable
+ receive callbacks again. */
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if(!stream->recv_header_complete) {
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: Headers complete!\n"));
+ if(!msh3request_ensure_room(stream, 2)) {
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ stream->recv_buf[stream->recv_header_len++] = '\r';
+ stream->recv_buf[stream->recv_header_len++] = '\n';
+ stream->recv_header_complete = true;
+ cur_recv_len += 2;
+ }
+ if(!msh3request_ensure_room(stream, *Length)) {
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ memcpy(stream->recv_buf + cur_recv_len, Data, *Length);
+ stream->recv_data_len += (size_t)*Length;
+ data->state.drain = 1;
+
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+ return true;
+}
+
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+
+ (void)Request;
+ (void)AbortError;
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: complete, aborted=%s\n",
+ Aborted ? "true" : "false"));
+ msh3_lock_acquire(&stream->recv_lock);
+ if(Aborted) {
+ stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
+ }
+ stream->recv_header_complete = true;
+ stream->recv_data_complete = true;
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
+ void *IfContext)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ (void)Request;
+ (void)stream;
+}
+
+static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
+ void *IfContext, void *SendContext)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ (void)Request;
+ (void)stream;
+ (void)SendContext;
+}
+
+static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ size_t outsize = 0;
+
+ (void)cf;
+ DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
+
+ if(stream->recv_error) {
+ failf(data, "request aborted");
+ data->state.drain = 0;
+ *err = stream->recv_error;
+ return -1;
+ }
+
+ *err = CURLE_OK;
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if(stream->recv_header_len) {
+ outsize = len;
+ if(stream->recv_header_len < outsize) {
+ outsize = stream->recv_header_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_header_len + stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_header_len + stream->recv_data_len - outsize);
+ }
+ stream->recv_header_len -= outsize;
+ DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of header", outsize));
+ }
+ else if(stream->recv_data_len) {
+ outsize = len;
+ if(stream->recv_data_len < outsize) {
+ outsize = stream->recv_data_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_data_len - outsize);
+ }
+ stream->recv_data_len -= outsize;
+ DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of data", outsize));
+ if(stream->recv_data_len == 0 && stream->recv_data_complete)
+ data->state.drain = 1;
+ }
+ else if(stream->recv_data_complete) {
+ DEBUGF(LOG_CF(data, cf, "req: receive complete"));
+ data->state.drain = 0;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
+ *err = CURLE_AGAIN;
+ outsize = -1;
+ }
+
+ msh3_lock_release(&stream->recv_lock);
+
+ return (ssize_t)outsize;
+}
+
+static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ struct h2h3req *hreq;
+ size_t hdrlen = 0;
+ size_t sentlen = 0;
+
+ /* Sizes must match for cast below to work" */
+ DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
+ DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
+
+ if(!stream->req) {
+ /* The first send on the request contains the headers and possibly some
+ data. Parse out the headers and create the request, then if there is
+ any data left over go ahead and send it too. */
+
+ *err = msh3_data_setup(cf, data);
+ if(*err) {
+ failf(data, "could not setup data");
+ return -1;
+ }
+
+ *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
+ if(*err) {
+ failf(data, "Curl_pseudo_headers failed");
+ return -1;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries));
+ stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
+ (MSH3_HEADER*)hreq->header, hreq->entries,
+ hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
+ MSH3_REQUEST_FLAG_NONE);
+ Curl_pseudo_free(hreq);
+ if(!stream->req) {
+ failf(data, "request open failed");
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ *err = CURLE_OK;
+ return len;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
+ if(len > 0xFFFFFFFF) {
+ /* msh3 doesn't support size_t sends currently. */
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ /* TODO - Need an explicit signal to know when to FIN. */
+ if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len,
+ stream)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ /* TODO - msh3/msquic will hold onto this memory until the send complete
+ event. How do we make sure curl doesn't free it until then? */
+ sentlen += len;
+ *err = CURLE_OK;
+ return sentlen;
+}
+
+static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ int bitmap = GETSOCK_BLANK;
+
+ if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
+ socks[0] = ctx->sock[SP_LOCAL];
+
+ if(stream->recv_error) {
+ bitmap |= GETSOCK_READSOCK(0);
+ data->state.drain = 1;
+ }
+ else if(stream->recv_header_len || stream->recv_data_len) {
+ bitmap |= GETSOCK_READSOCK(0);
+ data->state.drain = 1;
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "select_sock %u -> %d",
+ (uint32_t)data->state.drain, bitmap));
+
+ return bitmap;
+}
+
+static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ (void)cf;
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %hhu",
+ (bool)(stream->recv_header_len || stream->recv_data_len)));
+ return stream->recv_header_len || stream->recv_data_len;
+}
+
+static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ /* use this socket from now on */
+ cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
+ /* the first socket info gets set at conn and data */
+ if(cf->sockindex == FIRSTSOCKET) {
+ cf->conn->remote_addr = &ctx->addr;
+ #ifdef ENABLE_IPV6
+ cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
+ #endif
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ }
+ ctx->active = TRUE;
+}
+
+static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_SETUP:
+ result = msh3_data_setup(cf, data);
+ break;
+ case CF_CTRL_DATA_DONE:
+ DEBUGF(LOG_CF(data, cf, "req: done"));
+ if(stream) {
+ if(stream->recv_buf) {
+ Curl_safefree(stream->recv_buf);
+ msh3_lock_uninitialize(&stream->recv_lock);
+ }
+ if(stream->req) {
+ MsH3RequestClose(stream->req);
+ stream->req = ZERO_NULL;
+ }
+ }
+ break;
+ case CF_CTRL_DATA_DONE_SEND:
+ DEBUGF(LOG_CF(data, cf, "req: send done"));
+ stream->upload_done = TRUE;
+ break;
+ case CF_CTRL_CONN_INFO_UPDATE:
+ DEBUGF(LOG_CF(data, cf, "req: update info"));
+ cf_msh3_active(cf, data);
+ break;
+ case CF_CTRL_CONN_REPORT_STATS:
+ if(cf->sockindex == FIRSTSOCKET)
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ bool verify = !!cf->conn->ssl_config.verifypeer;
+ MSH3_ADDR addr = {0};
+ memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
+ ctx->verbose = (data && data->set.verbose);
+
+ if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) {
+ /* TODO: need a way to provide trust anchors to MSH3 */
+#ifdef DEBUGBUILD
+ /* we need this for our test cases to run */
+ DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
+ "switching off verifypeer in DEBUG mode"));
+ verify = 0;
+#else
+ DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
+ "attempting with built-in verification"));
+#endif
+ }
+
+ DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
+ cf->conn->host.name, (int)cf->conn->remote_port, verify));
+
+ ctx->api = MsH3ApiOpen();
+ if(!ctx->api) {
+ failf(data, "can't create msh3 api");
+ return CURLE_FAILED_INIT;
+ }
+
+ ctx->qconn = MsH3ConnectionOpen(ctx->api,
+ &msh3_conn_if,
+ ctx,
+ cf->conn->host.name,
+ &addr,
+ !verify);
+ if(!ctx->qconn) {
+ failf(data, "can't create msh3 connection");
+ if(ctx->api) {
+ MsH3ApiClose(ctx->api);
+ ctx->api = NULL;
+ }
+ return CURLE_FAILED_INIT;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
+ if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ *done = FALSE;
+ if(!ctx->qconn) {
+ ctx->connect_started = Curl_now();
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ }
+
+ if(ctx->handshake_complete) {
+ ctx->handshake_at = Curl_now();
+ if(ctx->handshake_succeeded) {
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ Curl_pgrsTime(data, TIMER_APPCONNECT);
+ }
+ else {
+ failf(data, "failed to connect, handshake failed");
+ result = CURLE_COULDNT_CONNECT;
+ }
+ }
+
+out:
+ return result;
+}
+
+static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ (void)data;
+ if(ctx) {
+ DEBUGF(LOG_CF(data, cf, "destroying"));
+ if(ctx->qconn)
+ MsH3ConnectionClose(ctx->qconn);
+ if(ctx->api)
+ MsH3ApiClose(ctx->api);
+
+ if(ctx->active) {
+ /* We share our socket at cf->conn->sock[cf->sockindex] when active.
+ * If it is no longer there, someone has stolen (and hopefully
+ * closed it) and we just forget about it.
+ */
+ if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
+ DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
+ (int)ctx->sock[SP_LOCAL]));
+ cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
+ "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ }
+ if(cf->sockindex == FIRSTSOCKET)
+ cf->conn->remote_addr = NULL;
+ }
+ if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
+ sclose(ctx->sock[SP_LOCAL]);
+ }
+ if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
+ sclose(ctx->sock[SP_REMOTE]);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ }
+}
+
+static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ cf_msh3_close(cf, data);
+ free(cf->ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ /* TODO: we do not have access to this so far, fake it */
+ (void)ctx;
+ *pres1 = 100;
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ (void)data;
+ return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
+ ctx->connected;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_msh3_destroy,
+ cf_msh3_connect,
+ cf_msh3_close,
+ Curl_cf_def_get_host,
+ cf_msh3_get_select_socks,
+ cf_msh3_data_pending,
+ cf_msh3_send,
+ cf_msh3_recv,
+ cf_msh3_data_event,
+ cf_msh3_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_msh3_query,
+};
+
+CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_msh3_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ (void)ai; /* TODO: msh3 resolves itself? */
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+bool Curl_conn_is_msh3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif /* USE_MSH3 */
diff --git a/Utilities/cmcurl/lib/vquic/msh3.h b/Utilities/cmcurl/lib/vquic/curl_msh3.h
index ce884d92dd..33931f59bb 100644
--- a/Utilities/cmcurl/lib/vquic/msh3.h
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_MSH3_H
-#define HEADER_CURL_VQUIC_MSH3_H
+#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H
+#define HEADER_CURL_VQUIC_CURL_MSH3_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,11 +30,17 @@
#include <msh3.h>
-struct quicsocket {
- MSH3_API* api;
- MSH3_CONNECTION* conn;
-};
+void Curl_msh3_ver(char *p, size_t len);
+
+CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_msh3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
#endif /* USE_MSQUIC */
-#endif /* HEADER_CURL_VQUIC_MSH3_H */
+#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
new file mode 100644
index 0000000000..ffdaeade35
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -0,0 +1,2515 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_NGTCP2
+#include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <ngtcp2/ngtcp2_crypto_boringssl.h>
+#else
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#endif
+#include "vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+#include "vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
+#include "vtls/wolfssl.h"
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "dynbuf.h"
+#include "select.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "h2h3.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "curl_ngtcp2.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define H3_ALPN_H3_29 "\x5h3-29"
+#define H3_ALPN_H3 "\x2h3"
+
+/*
+ * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
+ * It is used as a circular buffer. Add new bytes at the end until it reaches
+ * the far end, then start over at index 0 again.
+ */
+
+#define H3_SEND_SIZE (256*1024)
+struct h3out {
+ uint8_t buf[H3_SEND_SIZE];
+ size_t used; /* number of bytes used in the buffer */
+ size_t windex; /* index in the buffer where to start writing the next
+ data block */
+};
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
+#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
+
+#ifdef USE_OPENSSL
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+#elif defined(USE_GNUTLS)
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+#elif defined(USE_WOLFSSL)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+#endif
+
+
+/*
+ * Store ngtcp2 version info in this buffer.
+ */
+void Curl_ngtcp2_ver(char *p, size_t len)
+{
+ const ngtcp2_info *ng2 = ngtcp2_version(0);
+ const nghttp3_info *ht3 = nghttp3_version(0);
+ (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
+ ng2->version_str, ht3->version_str);
+}
+
+struct cf_ngtcp2_ctx {
+ struct cf_quic_ctx q;
+ 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_connection_close_error last_error;
+ ngtcp2_crypto_conn_ref conn_ref;
+#ifdef USE_OPENSSL
+ SSL_CTX *sslctx;
+ SSL *ssl;
+#elif defined(USE_GNUTLS)
+ struct gtls_instance *gtls;
+#elif defined(USE_WOLFSSL)
+ WOLFSSL_CTX *sslctx;
+ WOLFSSL *ssl;
+#endif
+ struct cf_call_data call_data;
+ nghttp3_conn *h3conn;
+ nghttp3_settings h3settings;
+ int qlogfd;
+ struct curltime started_at; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ struct curltime reconnect_at; /* time the next attempt should start */
+ BIT(got_first_byte); /* if first byte was received */
+};
+
+/* How to access `call_data` from a cf_ngtcp2 filter */
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
+
+
+/* ngtcp2 default congestion controller does not perform pacing. Limit
+ the maximum packet burst to MAX_PKT_BURST packets. */
+#define MAX_PKT_BURST 10
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data);
+
+static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
+{
+ struct Curl_cfilter *cf = conn_ref->user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ return ctx->qconn;
+}
+
+static ngtcp2_tstamp timestamp(void)
+{
+ struct curltime ct = Curl_now();
+ return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+#ifdef DEBUG_NGTCP2
+static void quic_printf(void *user_data, const char *fmt, ...)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
+ (void)ctx; /* TODO: need an easy handle to infof() message */
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void qlog_callback(void *user_data, uint32_t flags,
+ const void *data, size_t datalen)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ (void)flags;
+ if(ctx->qlogfd != -1) {
+ ssize_t rc = write(ctx->qlogfd, data, datalen);
+ if(rc == -1) {
+ /* on write error, stop further write attempts */
+ close(ctx->qlogfd);
+ ctx->qlogfd = -1;
+ }
+ }
+
+}
+
+static void quic_settings(struct cf_ngtcp2_ctx *ctx,
+ struct Curl_easy *data)
+{
+ ngtcp2_settings *s = &ctx->settings;
+ ngtcp2_transport_params *t = &ctx->transport_params;
+ size_t stream_win_size = CURL_MAX_READ_SIZE;
+
+ ngtcp2_settings_default(s);
+ ngtcp2_transport_params_default(t);
+#ifdef DEBUG_NGTCP2
+ s->log_printf = quic_printf;
+#else
+ s->log_printf = NULL;
+#endif
+
+ (void)data;
+ s->initial_ts = timestamp();
+ s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
+ s->max_window = 100 * stream_win_size;
+ s->max_stream_window = stream_win_size;
+
+ t->initial_max_data = 10 * stream_win_size;
+ t->initial_max_stream_data_bidi_local = stream_win_size;
+ t->initial_max_stream_data_bidi_remote = stream_win_size;
+ t->initial_max_stream_data_uni = stream_win_size;
+ t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
+ t->initial_max_streams_uni = QUIC_MAX_STREAMS;
+ t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
+ if(ctx->qlogfd != -1) {
+ s->qlog.write = qlog_callback;
+ }
+}
+
+#ifdef USE_OPENSSL
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#elif defined(USE_GNUTLS)
+static int keylog_callback(gnutls_session_t session, const char *label,
+ const gnutls_datum_t *secret)
+{
+ gnutls_datum_t crandom;
+ gnutls_datum_t srandom;
+
+ gnutls_session_get_random(session, &crandom, &srandom);
+ if(crandom.size != 32) {
+ return -1;
+ }
+
+ Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+ return 0;
+}
+#elif defined(USE_WOLFSSL)
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#endif
+#endif
+
+static int init_ngh3_conn(struct Curl_cfilter *cf);
+
+#ifdef USE_OPENSSL
+static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
+ struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct connectdata *conn = cf->conn;
+ CURLcode result = CURLE_FAILED_INIT;
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+ if(!ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
+ goto out;
+ }
+#else
+ if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
+ goto out;
+ }
+#endif
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_curves_list failed");
+ goto out;
+ }
+#else
+ if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ goto out;
+ }
+
+ if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ goto out;
+ }
+#endif
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
+ if(result)
+ goto out;
+
+ /* OpenSSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ *pssl_ctx = result? NULL : ssl_ctx;
+ if(result && ssl_ctx)
+ SSL_CTX_free(ssl_ctx);
+ return result;
+}
+
+static CURLcode quic_set_client_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ SSL_CTX *ssl_ctx = ctx->sslctx;
+ const struct ssl_config_data *ssl_config;
+
+ ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
+ DEBUGASSERT(ssl_config);
+
+ if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
+ || ssl_config->cert_type) {
+ return Curl_ossl_set_client_cert(
+ data, ssl_ctx, ssl_config->primary.clientcert,
+ ssl_config->primary.cert_blob, ssl_config->cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd);
+ }
+
+ return CURLE_OK;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+
+ (void)data;
+ DEBUGASSERT(!ctx->ssl);
+ ctx->ssl = SSL_new(ctx->sslctx);
+
+ SSL_set_app_data(ctx->ssl, &ctx->conn_ref);
+ SSL_set_connect_state(ctx->ssl);
+ SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+ return CURLE_OK;
+}
+#elif defined(USE_GNUTLS)
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ gnutls_datum_t alpn[2];
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = cf->conn->host.name;
+ long * const pverifyresult = &data->set.ssl.certverifyresult;
+ int rc;
+
+ DEBUGASSERT(ctx->gtls == NULL);
+ ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
+ if(!ctx->gtls)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl,
+ hostname, ctx->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
+
+ if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+ DEBUGF(LOG_CF(data, cf,
+ "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
+ if(rc < 0) {
+ DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+ gnutls_strerror(rc)));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
+ }
+
+ /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
+ alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
+ alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
+ alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
+ alpn[1].size = sizeof(H3_ALPN_H3) - 2;
+
+ gnutls_alpn_set_protocols(ctx->gtls->session,
+ alpn, 2, GNUTLS_ALPN_MANDATORY);
+ return CURLE_OK;
+}
+#elif defined(USE_WOLFSSL)
+
+static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx,
+ struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct connectdata *conn = cf->conn;
+ CURLcode result = CURLE_FAILED_INIT;
+ WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+
+ if(!ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
+ goto out;
+ }
+
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ goto out;
+ }
+
+ if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+#if defined(HAVE_SECRET_CALLBACK)
+ wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+#else
+ failf(data, "wolfSSL was built without keylog callback");
+ goto out;
+#endif
+ }
+
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ if(conn->ssl_config.CAfile || conn->ssl_config.CApath) {
+ /* tell wolfSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ goto out;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use wolfssl's built-in default as fallback */
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ else {
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ *pssl_ctx = result? NULL : ssl_ctx;
+ if(result && ssl_ctx)
+ SSL_CTX_free(ssl_ctx);
+ return result;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = cf->conn->host.name;
+
+ (void)data;
+ DEBUGASSERT(!ctx->ssl);
+ ctx->ssl = wolfSSL_new(ctx->sslctx);
+
+ wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref);
+ wolfSSL_set_connect_state(ctx->ssl);
+ wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
+ hostname, (unsigned short)strlen(hostname));
+
+ return CURLE_OK;
+}
+#endif /* defined(USE_WOLFSSL) */
+
+static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
+{
+ (void)user_data;
+ (void)tconn;
+ return 0;
+}
+
+static void report_consumed_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ size_t consumed)
+{
+ struct HTTP *stream = data->req.p.http;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
+ /* the HTTP/1.1 response headers are written to the buffer, but
+ * consuming those does not count against flow control. */
+ if(stream->recv_buf_nonflow) {
+ if(consumed >= stream->recv_buf_nonflow) {
+ consumed -= stream->recv_buf_nonflow;
+ stream->recv_buf_nonflow = 0;
+ }
+ else {
+ stream->recv_buf_nonflow -= consumed;
+ consumed = 0;
+ }
+ }
+ if(consumed > 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes",
+ stream->stream3_id, consumed));
+ ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->stream3_id,
+ consumed);
+ ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
+ }
+ if(!stream->closed && data->state.drain
+ && !stream->memlen
+ && !Curl_dyn_len(&stream->overflow)) {
+ /* nothing buffered any more */
+ data->state.drain = 0;
+ }
+}
+
+static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ nghttp3_ssize nconsumed;
+ int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
+ struct Curl_easy *data = stream_user_data;
+ (void)offset;
+ (void)data;
+
+ nconsumed =
+ nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd",
+ stream_id, buflen, nconsumed));
+ if(nconsumed < 0) {
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error,
+ nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* number of bytes inside buflen which consists of framing overhead
+ * including QPACK HEADERS. In other words, it does not consume payload of
+ * DATA frame. */
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
+ ngtcp2_conn_extend_max_offset(tconn, nconsumed);
+
+ return 0;
+}
+
+static int
+cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t offset, uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)stream_id;
+ (void)tconn;
+ (void)offset;
+ (void)datalen;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream3_id, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+
+ (void)tconn;
+ (void)data;
+ /* stream is closed... */
+
+ if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+ app_error_code = NGHTTP3_H3_NO_ERROR;
+ }
+
+ rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id,
+ app_error_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%"
+ PRIu64 ") -> %d", stream3_id, app_error_code, rv));
+ if(rv) {
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data = stream_user_data;
+ int rv;
+ (void)tconn;
+ (void)final_size;
+ (void)app_error_code;
+ (void)data;
+
+ rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)tconn;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
+ uint64_t max_streams,
+ void *user_data)
+{
+ (void)tconn;
+ (void)max_streams;
+ (void)user_data;
+
+ return 0;
+}
+
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)tconn;
+ (void)max_data;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static void cb_rand(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx)
+{
+ CURLcode result;
+ (void)rand_ctx;
+
+ result = Curl_rand(NULL, dest, destlen);
+ if(result) {
+ /* cb_rand is only used for non-cryptographic context. If Curl_rand
+ failed, just fill 0 and call it *random*. */
+ memset(dest, 0, destlen);
+ }
+}
+
+static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data)
+{
+ CURLcode result;
+ (void)tconn;
+ (void)user_data;
+
+ result = Curl_rand(NULL, cid->data, cidlen);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ cid->datalen = cidlen;
+
+ result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+}
+
+static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
+ void *user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ (void)tconn;
+
+ if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
+ return 0;
+ }
+
+ if(init_ngh3_conn(cf) != CURLE_OK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static ngtcp2_callbacks ng_callbacks = {
+ ngtcp2_crypto_client_initial_cb,
+ NULL, /* recv_client_initial */
+ ngtcp2_crypto_recv_crypto_data_cb,
+ cb_handshake_completed,
+ NULL, /* recv_version_negotiation */
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ cb_recv_stream_data,
+ cb_acked_stream_data_offset,
+ NULL, /* stream_open */
+ cb_stream_close,
+ NULL, /* recv_stateless_reset */
+ ngtcp2_crypto_recv_retry_cb,
+ cb_extend_max_local_streams_bidi,
+ NULL, /* extend_max_local_streams_uni */
+ cb_rand,
+ cb_get_new_connection_id,
+ NULL, /* remove_connection_id */
+ ngtcp2_crypto_update_key_cb, /* update_key */
+ NULL, /* path_validation */
+ NULL, /* select_preferred_addr */
+ cb_stream_reset,
+ NULL, /* extend_max_remote_streams_bidi */
+ NULL, /* extend_max_remote_streams_uni */
+ cb_extend_max_stream_data,
+ NULL, /* dcid_status */
+ NULL, /* handshake_confirmed */
+ NULL, /* recv_new_token */
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ NULL, /* recv_datagram */
+ NULL, /* ack_datagram */
+ NULL, /* lost_datagram */
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ cb_stream_stop_sending,
+ NULL, /* version_negotiation */
+ cb_recv_rx_key,
+ NULL, /* recv_tx_key */
+ NULL, /* early_data_rejected */
+};
+
+static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
+ int rv = GETSOCK_BLANK;
+ struct HTTP *stream = data->req.p.http;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ socks[0] = ctx->q.sockfd;
+
+ /* in an HTTP/3 connection we can basically always get a frame so we should
+ always be ready for one */
+ rv |= GETSOCK_READSOCK(0);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND &&
+ (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
+ ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
+ ngtcp2_conn_get_max_data_left(ctx->qconn) &&
+ nghttp3_conn_is_stream_writable(ctx->h3conn, stream->stream3_id))
+ rv |= GETSOCK_WRITESOCK(0);
+
+ DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
+ rv, (int)socks[0]));
+ CF_DATA_RESTORE(cf, save);
+ return rv;
+}
+
+static void notify_drain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ if(!data->state.drain) {
+ data->state.drain = 1;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+}
+
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)conn;
+ (void)stream_id;
+ (void)app_error_code;
+ (void)cf;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRIx64 ")",
+ stream_id, app_error_code));
+ stream->closed = TRUE;
+ stream->error3 = app_error_code;
+ if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) {
+ /* TODO: we do not get a specific error when the remote end closed
+ * the response before it was complete. */
+ stream->reset = TRUE;
+ }
+ notify_drain(cf, data);
+ return 0;
+}
+
+/*
+ * write_resp_raw() copies resonse data in raw format to the `data`'s
+ * receive buffer. If not enough space is available, it appends to the
+ * `data`'s overflow buffer.
+ */
+static CURLcode write_resp_raw(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem, size_t memlen,
+ bool flow)
+{
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ const char *buf = mem;
+ size_t ncopy = memlen;
+ /* copy as much as possible to the receive buffer */
+ if(stream->len) {
+ size_t len = CURLMIN(ncopy, stream->len);
+ memcpy(stream->mem + stream->memlen, buf, len);
+ stream->len -= len;
+ stream->memlen += len;
+ buf += len;
+ ncopy -= len;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
+ " to data buffer", stream->stream3_id, len));
+ }
+ /* copy the rest to the overflow buffer */
+ if(ncopy) {
+ result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
+ " to overflow buffer -> %d",
+ stream->stream3_id, ncopy, result));
+ notify_drain(cf, data);
+ }
+
+ if(!flow)
+ stream->recv_buf_nonflow += memlen;
+ if(CF_DATA_CURRENT(cf) != data) {
+ notify_drain(cf, data);
+ }
+ return result;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ CURLcode result;
+
+ (void)conn;
+ (void)stream3_id;
+
+ result = write_resp_raw(cf, data, buf, buflen, TRUE);
+ return result? -1 : 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;
+ (void)conn;
+ (void)stream_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);
+ return 0;
+}
+
+/* Decode HTTP status code. Returns -1 if no valid status code was
+ decoded. (duplicate from http2.c) */
+static int decode_status_code(const uint8_t *value, size_t len)
+{
+ int i;
+ int res;
+
+ if(len != 3) {
+ return -1;
+ }
+
+ res = 0;
+
+ for(i = 0; i < 3; ++i) {
+ char c = value[i];
+
+ if(c < '0' || c > '9') {
+ return -1;
+ }
+
+ res *= 10;
+ res += c - '0';
+ }
+
+ return res;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)fin;
+ (void)cf;
+
+ /* add a CRLF only if we've received some headers */
+ if(stream->firstheader) {
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d",
+ stream_id, stream->status_code));
+ if(stream->status_code / 100 != 1) {
+ stream->bodystarted = TRUE;
+ }
+ return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+ nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)flags;
+ (void)cf;
+
+ if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
+ char line[14]; /* status line is always 13 characters long */
+ size_t ncopy;
+
+ DEBUGASSERT(!stream->firstheader);
+ stream->status_code = decode_status_code(h3val.base, h3val.len);
+ DEBUGASSERT(stream->status_code != -1);
+ ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
+ stream->status_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s",
+ stream_id, line));
+ result = write_resp_raw(cf, data, line, ncopy, FALSE);
+ if(result) {
+ return -1;
+ }
+ stream->firstheader = TRUE;
+ }
+ else {
+ /* store as an HTTP1-style header */
+ DEBUGASSERT(stream->firstheader);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s",
+ stream_id, (int)h3name.len, h3name.base,
+ (int)h3val.len, h3val.base));
+ result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, ": ", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int cb_h3_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, stream_id, app_error_code);
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_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;
+ (void)data;
+
+ rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id,
+ app_error_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static nghttp3_callbacks ngh3_callbacks = {
+ cb_h3_acked_stream_data, /* 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 */
+};
+
+static int init_ngh3_conn(struct Curl_cfilter *cf)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ int rc;
+ int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
+
+ if(ngtcp2_conn_get_max_local_streams_uni(ctx->qconn) < 3) {
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ nghttp3_settings_default(&ctx->h3settings);
+
+ rc = nghttp3_conn_client_new(&ctx->h3conn,
+ &ngh3_callbacks,
+ &ctx->h3settings,
+ nghttp3_mem_default(),
+ cf);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id,
+ qpack_dec_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ return CURLE_OK;
+ fail:
+
+ return result;
+}
+
+static void drain_overflow_buffer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ size_t overlen = Curl_dyn_len(&stream->overflow);
+ size_t ncopy = CURLMIN(overlen, stream->len);
+
+ (void)cf;
+ if(ncopy > 0) {
+ memcpy(stream->mem + stream->memlen,
+ Curl_dyn_ptr(&stream->overflow), ncopy);
+ stream->len -= ncopy;
+ stream->memlen += ncopy;
+ if(ncopy != overlen)
+ /* make the buffer only keep the tail */
+ (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
+ else {
+ Curl_dyn_reset(&stream->overflow);
+ }
+ }
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+
+ (void)cf;
+
+ if(stream->reset) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
+ *err = CURLE_PARTIAL_FILE;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
+ stream->stream3_id, *err));
+ goto out;
+ }
+ else if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was not closed cleanly: (err 0x%" PRIx64
+ ")",
+ stream->stream3_id, stream->error3);
+ *err = CURLE_HTTP3;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+
+ if(!stream->bodystarted) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->stream3_id);
+ *err = CURLE_HTTP3;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
+ " -> %d", stream->stream3_id, *err));
+ }
+ *err = CURLE_OK;
+ nread = 0;
+
+out:
+ data->state.drain = 0;
+ return nread;
+}
+
+/* incoming data frames on the h3 stream */
+static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+ struct cf_call_data save;
+
+ (void)ctx;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(ctx->qconn);
+ DEBUGASSERT(ctx->h3conn);
+ *err = CURLE_OK;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) start",
+ stream->stream3_id, len));
+ /* TODO: this implementation of response DATA buffering is fragile.
+ * It makes the following assumptions:
+ * - the `buf` passed here has the same lifetime as the easy handle
+ * - data returned in `buf` from this call is immediately used and `buf`
+ * can be overwritten during any handling of other transfers at
+ * this connection.
+ */
+ if(!stream->memlen) {
+ /* `buf` was not known before or is currently not used by stream,
+ * assign it (again). */
+ stream->mem = buf;
+ stream->len = len;
+ }
+
+ /* if there's data in the overflow buffer, move as much
+ as possible to the receive buffer now */
+ drain_overflow_buffer(cf, data);
+
+ if(cf_process_ingress(cf, data)) {
+ *err = CURLE_RECV_ERROR;
+ nread = -1;
+ goto out;
+ }
+
+ if(stream->memlen) {
+ nread = stream->memlen;
+ /* reset to allow more data to come */
+ /* TODO: very brittle buffer use design:
+ * - stream->mem has now `nread` bytes of response data
+ * - we assume that the caller will use those immediately and
+ * we can overwrite that with new data on our next invocation from
+ * anywhere.
+ */
+ stream->mem = buf;
+ stream->memlen = 0;
+ stream->len = len;
+ /* extend the stream window with the data we're consuming and send out
+ any additional packets to tell the server that we can receive more */
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> %zd bytes",
+ stream->stream3_id, nread));
+ report_consumed_data(cf, data, nread);
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ nread = -1;
+ }
+ goto out;
+ }
+
+ if(stream->closed) {
+ nread = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> EAGAIN",
+ stream->stream3_id));
+ *err = CURLE_AGAIN;
+ nread = -1;
+out:
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ nread = -1;
+ goto out;
+ }
+
+ CF_DATA_RESTORE(cf, save);
+ return nread;
+}
+
+/* this amount of data has now been acked on this stream */
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)user_data;
+
+ (void)cf;
+ if(!data->set.postfields) {
+ stream->h3out->used -= datalen;
+ DEBUGF(LOG_CF(data, cf, "cb_h3_acked_stream_data, %"PRIu64" bytes, "
+ "%zd left unacked", datalen, stream->h3out->used));
+ DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
+
+ if(stream->h3out->used == 0) {
+ int rv = nghttp3_conn_resume_stream(conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ }
+ return 0;
+}
+
+static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ size_t nread;
+ struct HTTP *stream = data->req.p.http;
+ (void)cf;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)veccnt;
+
+ if(data->set.postfields) {
+ vec[0].base = data->set.postfields;
+ vec[0].len = data->state.infilesize;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 1;
+ }
+
+ if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+
+ nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
+ if(nread > 0) {
+ /* nghttp3 wants us to hold on to the data until it tells us it is okay to
+ delete it. Append the data at the end of the h3out buffer. Since we can
+ only return consecutive data, copy the amount that fits and the next
+ part comes in next invoke. */
+ struct h3out *out = stream->h3out;
+ if(nread + out->windex > H3_SEND_SIZE)
+ nread = H3_SEND_SIZE - out->windex;
+
+ memcpy(&out->buf[out->windex], stream->upload_mem, nread);
+
+ /* that's the chunk we return to nghttp3 */
+ vec[0].base = &out->buf[out->windex];
+ vec[0].len = nread;
+
+ out->windex += nread;
+ out->used += nread;
+
+ if(out->windex == H3_SEND_SIZE)
+ out->windex = 0; /* wrap */
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data->state.infilesize != -1) {
+ stream->upload_left -= nread;
+ if(!stream->upload_left)
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
+ nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ out->used));
+ }
+ if(stream->upload_done && !stream->upload_len &&
+ (stream->upload_left <= 0)) {
+ DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction sets EOF"));
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return nread ? 1 : 0;
+ }
+ else if(!nread) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ return 1;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode h3_stream_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ CURLcode result = CURLE_OK;
+ nghttp3_nv *nva = NULL;
+ int64_t stream3_id;
+ int rc = 0;
+ struct h3out *h3out = NULL;
+ struct h2h3req *hreq = NULL;
+
+ rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream3_id, NULL);
+ if(rc) {
+ failf(data, "can get bidi streams");
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ stream->h3req = TRUE;
+ Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
+ stream->recv_buf_nonflow = 0;
+
+ result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(nghttp3_nv) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].namelen = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].valuelen = hreq->header[i].valuelen;
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT: {
+ nghttp3_data_reader data_reader;
+ 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, but not zero */
+
+ data_reader.read_data = cb_h3_readfunction;
+
+ h3out = calloc(sizeof(struct h3out), 1);
+ if(!h3out) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ stream->h3out = h3out;
+
+ rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
+ nva, nheader, &data_reader, data);
+ if(rc)
+ goto fail;
+ break;
+ }
+ default:
+ stream->upload_left = 0; /* nothing left to send */
+ rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
+ nva, nheader, NULL, data);
+ if(rc)
+ goto fail;
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
+ stream3_id, (void *)data);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
+ stream3_id, data->state.url));
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ if(rc) {
+ switch(rc) {
+ case NGHTTP3_ERR_CONN_CLOSING:
+ DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+ "connection is closing", stream->stream3_id));
+ result = CURLE_RECV_ERROR;
+ break;
+ default:
+ DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+ stream->stream3_id, rc, ngtcp2_strerror(rc)));
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ }
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+
+static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ ssize_t sent = 0;
+ struct HTTP *stream = data->req.p.http;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx->qconn);
+ DEBUGASSERT(ctx->h3conn);
+ *err = CURLE_OK;
+
+ if(stream->closed) {
+ *err = CURLE_HTTP3;
+ sent = -1;
+ goto out;
+ }
+
+ if(!stream->h3req) {
+ CURLcode result = h3_stream_open(cf, data, buf, len);
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", result));
+ sent = -1;
+ goto out;
+ }
+ /* Assume that mem of length len only includes HTTP/1.1 style
+ header fields. In other words, it does not contain request
+ body. */
+ sent = len;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "ngh3_stream_send() wants to send %zd bytes",
+ len));
+ if(!stream->upload_len) {
+ stream->upload_mem = buf;
+ stream->upload_len = len;
+ (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
+ }
+ else {
+ *err = CURLE_AGAIN;
+ sent = -1;
+ goto out;
+ }
+ }
+
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ sent = -1;
+ goto out;
+ }
+
+ /* Reset post upload buffer after resumed. */
+ if(stream->upload_mem) {
+ if(data->set.postfields) {
+ sent = len;
+ }
+ else {
+ sent = len - stream->upload_len;
+ }
+
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+
+ if(sent == 0) {
+ *err = CURLE_AGAIN;
+ sent = -1;
+ goto out;
+ }
+ }
+out:
+ CF_DATA_RESTORE(cf, save);
+ return sent;
+}
+
+static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ const char *hostname, *disp_hostname;
+ int port;
+ char *snihost;
+
+ Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port);
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ if(cf->conn->ssl_config.verifyhost) {
+#ifdef USE_OPENSSL
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(ctx->ssl);
+ if(!server_cert) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ return result;
+#elif defined(USE_GNUTLS)
+ result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+ &cf->conn->ssl_config, &data->set.ssl,
+ hostname, disp_hostname,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE)
+ return CURLE_PEER_FAILED_VERIFICATION;
+#endif
+ infof(data, "Verified certificate just fine");
+ }
+ else
+ infof(data, "Skipped certificate verification");
+#ifdef USE_OPENSSL
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, ctx->ssl);
+#endif
+ return result;
+}
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ ssize_t recvd;
+ int rv;
+ uint8_t buf[65536];
+ size_t bufsize = sizeof(buf);
+ size_t pktcount = 0, total_recvd = 0;
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ ngtcp2_path path;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_pkt_info pi = { 0 };
+
+ for(;;) {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
+ goto out;
+ }
+ if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ const char *r_ip;
+ int r_port;
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ failf(data, "ngtcp2: connection to %s port %u refused",
+ r_ip, r_port);
+ return CURLE_COULDNT_CONNECT;
+ }
+ failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd (errno=%d)",
+ recvd, SOCKERRNO);
+ return CURLE_RECV_ERROR;
+ }
+
+ if(recvd > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+
+ ++pktcount;
+ total_recvd += recvd;
+
+ 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);
+
+ rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, buf, recvd, ts);
+ if(rv) {
+ DEBUGF(LOG_CF(data, cf, "ingress, read_pkt -> %s",
+ ngtcp2_strerror(rv)));
+ if(!ctx->last_error.error_code) {
+ if(rv == NGTCP2_ERR_CRYPTO) {
+ ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ &ctx->last_error,
+ ngtcp2_conn_get_tls_alert(ctx->qconn), NULL, 0);
+ }
+ else {
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &ctx->last_error, rv, NULL, 0);
+ }
+ }
+
+ 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;
+ }
+ }
+
+out:
+ (void)pktcount;
+ (void)total_recvd;
+ DEBUGF(LOG_CF(data, cf, "ingress, recvd %zu packets with %zd bytes",
+ pktcount, total_recvd));
+ return CURLE_OK;
+}
+
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ size_t sent;
+ ngtcp2_ssize outlen;
+ uint8_t *outpos = ctx->q.pktbuf;
+ size_t max_udp_payload_size =
+ ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn);
+ size_t path_max_udp_payload_size =
+ ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn);
+ size_t max_pktcnt =
+ CURLMIN(MAX_PKT_BURST, ctx->q.pktbuflen / max_udp_payload_size);
+ size_t pktcnt = 0;
+ size_t gsolen = 0; /* this disables gso until we have a clue */
+ ngtcp2_path_storage ps;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_tstamp expiry;
+ ngtcp2_duration timeout;
+ int64_t stream_id;
+ nghttp3_ssize veccnt;
+ int fin;
+ nghttp3_vec vec[16];
+ ngtcp2_ssize ndatalen;
+ uint32_t flags;
+ CURLcode curlcode;
+
+ rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts);
+ if(rv) {
+ failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
+ ngtcp2_strerror(rv));
+ ngtcp2_connection_close_error_set_transport_error_liberr(&ctx->last_error,
+ rv, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+
+ if(ctx->q.num_blocked_pkt) {
+ curlcode = vquic_send_blocked_pkt(cf, data, &ctx->q);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+
+ ngtcp2_path_storage_zero(&ps);
+
+ for(;;) {
+ veccnt = 0;
+ stream_id = -1;
+ fin = 0;
+
+ if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
+ veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
+ sizeof(vec) / sizeof(vec[0]));
+ if(veccnt < 0) {
+ failf(data, "nghttp3_conn_writev_stream returned error: %s",
+ nghttp3_strerror((int)veccnt));
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error,
+ nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
+ (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
+ outlen = ngtcp2_conn_writev_stream(ctx->qconn, &ps.path, NULL, outpos,
+ max_udp_payload_size,
+ &ndatalen, flags, stream_id,
+ (const ngtcp2_vec *)vec, veccnt, ts);
+ if(outlen == 0) {
+ /* ngtcp2 does not want to send more packets, if the buffer is
+ * not empty, send that now */
+ if(outpos != ctx->q.pktbuf) {
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - ctx->q.pktbuf - sent,
+ gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+ /* done for now */
+ goto out;
+ }
+ if(outlen < 0) {
+ switch(outlen) {
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ assert(ndatalen == -1);
+ nghttp3_conn_block_stream(ctx->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ assert(ndatalen == -1);
+ nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_WRITE_MORE:
+ /* ngtcp2 wants to send more. update the flow of the stream whose data
+ * is in the buffer and continue */
+ assert(ndatalen >= 0);
+ rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ default:
+ assert(ndatalen == -1);
+ failf(data, "ngtcp2_conn_writev_stream returned error: %s",
+ ngtcp2_strerror((int)outlen));
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &ctx->last_error, (int)outlen, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else if(ndatalen >= 0) {
+ /* ngtcp2 thinks it has added all it wants. Update the stream */
+ rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ /* advance to the end of the buffered packet data */
+ outpos += outlen;
+
+ if(pktcnt == 0) {
+ /* first packet buffer chunk. use this as gsolen. It's how ngtcp2
+ * indicates the intended segment size. */
+ gsolen = outlen;
+ }
+ else if((size_t)outlen > gsolen ||
+ (gsolen > path_max_udp_payload_size && (size_t)outlen != gsolen)) {
+ /* Packet larger than path_max_udp_payload_size is PMTUD probe
+ packet and it might not be sent because of EMSGSIZE. Send
+ them separately to minimize the loss. */
+ /* send the pktbuf *before* the last addition */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - outlen - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ /* blocked, add the pktbuf *before* and *at* the last addition
+ * separately to the blocked packages */
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - outlen - ctx->q.pktbuf - sent, gsolen);
+ vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* send the pktbuf *at* the last addition */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, outpos - outlen, outlen,
+ outlen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ assert(0 == sent);
+ vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* pktbuf has been completely sent */
+ pktcnt = 0;
+ outpos = ctx->q.pktbuf;
+ continue;
+ }
+
+ if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
+ /* enough packets or last one is shorter than the intended
+ * segment size, indicating that it is time to send. */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - ctx->q.pktbuf - sent, gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* pktbuf has been completely sent */
+ pktcnt = 0;
+ outpos = ctx->q.pktbuf;
+ }
+ }
+
+out:
+ /* non-errored exit. check when we should run again. */
+ expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+ if(expiry != UINT64_MAX) {
+ if(expiry <= ts) {
+ timeout = 0;
+ }
+ else {
+ timeout = expiry - ts;
+ if(timeout % NGTCP2_MILLISECONDS) {
+ timeout += NGTCP2_MILLISECONDS;
+ }
+ }
+ Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ /* We may have received more data than we're able to hold in the receive
+ buffer and allocated an overflow buffer. Since it's possible that
+ there's no more data coming on the socket, we need to keep reading
+ until the overflow buffer is empty. */
+ const struct HTTP *stream = data->req.p.http;
+ (void)cf;
+ return Curl_dyn_len(&stream->overflow) > 0;
+}
+
+static CURLcode cf_ngtcp2_data_event(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_DONE: {
+ struct HTTP *stream = data->req.p.http;
+ Curl_dyn_free(&stream->overflow);
+ free(stream->h3out);
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ struct HTTP *stream = data->req.p.http;
+ stream->upload_done = TRUE;
+ (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
+ break;
+ }
+ case CF_CTRL_DATA_IDLE:
+ if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) {
+ if(cf_flush_egress(cf, data)) {
+ result = CURLE_SEND_ERROR;
+ }
+ }
+ break;
+ case CF_CTRL_CONN_REPORT_STATS:
+ if(cf->sockindex == FIRSTSOCKET) {
+ if(ctx->got_first_byte)
+ Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at);
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
+ }
+ break;
+ default:
+ break;
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
+{
+ struct cf_call_data save = ctx->call_data;
+
+ if(ctx->qlogfd != -1) {
+ close(ctx->qlogfd);
+ ctx->qlogfd = -1;
+ }
+#ifdef USE_OPENSSL
+ if(ctx->ssl)
+ SSL_free(ctx->ssl);
+ if(ctx->sslctx)
+ SSL_CTX_free(ctx->sslctx);
+#elif defined(USE_GNUTLS)
+ if(ctx->gtls) {
+ if(ctx->gtls->cred)
+ gnutls_certificate_free_credentials(ctx->gtls->cred);
+ if(ctx->gtls->session)
+ gnutls_deinit(ctx->gtls->session);
+ free(ctx->gtls);
+ }
+#elif defined(USE_WOLFSSL)
+ if(ctx->ssl)
+ wolfSSL_free(ctx->ssl);
+ if(ctx->sslctx)
+ wolfSSL_CTX_free(ctx->sslctx);
+#endif
+ vquic_ctx_free(&ctx->q);
+ if(ctx->h3conn)
+ nghttp3_conn_del(ctx->h3conn);
+ if(ctx->qconn)
+ ngtcp2_conn_del(ctx->qconn);
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->call_data = save;
+}
+
+static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ if(ctx && ctx->qconn) {
+ char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
+ ngtcp2_tstamp ts;
+ ngtcp2_ssize rc;
+
+ DEBUGF(LOG_CF(data, cf, "close"));
+ ts = timestamp();
+ rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
+ NULL, /* pkt_info */
+ (uint8_t *)buffer, sizeof(buffer),
+ &ctx->last_error, ts);
+ if(rc > 0) {
+ while((send(ctx->q.sockfd, buffer, rc, 0) == -1) &&
+ SOCKERRNO == EINTR);
+ }
+
+ cf_ngtcp2_ctx_clear(ctx);
+ }
+
+ cf->connected = FALSE;
+ CF_DATA_RESTORE(cf, save);
+}
+
+static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ if(ctx) {
+ cf_ngtcp2_ctx_clear(ctx);
+ free(ctx);
+ }
+ cf->ctx = NULL;
+ /* No CF_DATA_RESTORE(cf, save) possible */
+}
+
+/*
+ * Might be called twice for happy eyeballs.
+ */
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rc;
+ int rv;
+ CURLcode result;
+ const struct Curl_sockaddr_ex *sockaddr;
+ int qfd;
+
+ ctx->version = NGTCP2_PROTO_VER_MAX;
+#ifdef USE_OPENSSL
+ result = quic_ssl_ctx(&ctx->sslctx, cf, data);
+ if(result)
+ return result;
+
+ result = quic_set_client_cert(cf, data);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ result = quic_ssl_ctx(&ctx->sslctx, cf, data);
+ if(result)
+ return result;
+#endif
+
+ result = quic_init_ssl(cf, data);
+ if(result)
+ return result;
+
+ 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);
+
+ result = vquic_ctx_init(&ctx->q,
+ NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
+ if(result)
+ return result;
+
+ Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+ &sockaddr, NULL, NULL, NULL, NULL);
+ 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->sa_addr, 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,
+ NULL, cf);
+ if(rc)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+#ifdef USE_GNUTLS
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session);
+#else
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl);
+#endif
+
+ ngtcp2_connection_close_error_default(&ctx->last_error);
+
+ ctx->conn_ref.get_conn = get_conn;
+ ctx->conn_ref.user_data = cf;
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+ struct curltime now;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect the UDP filter first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+
+ CF_DATA_SAVE(save, cf, data);
+
+ if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+ /* Not time yet to attempt the next connect */
+ DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ goto out;
+ }
+
+ if(!ctx->qconn) {
+ ctx->started_at = now;
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ result = cf_flush_egress(cf, data);
+ /* we do not expect to be able to recv anything yet */
+ goto out;
+ }
+
+ result = cf_process_ingress(cf, data);
+ if(result)
+ goto out;
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ goto out;
+
+ if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
+ ctx->handshake_at = now;
+ DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at)));
+ result = qng_verify_peer(cf, data);
+ if(!result) {
+ DEBUGF(LOG_CF(data, cf, "peer verified"));
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ }
+ }
+
+out:
+ if(result == CURLE_RECV_ERROR && ctx->qconn &&
+ ngtcp2_conn_is_in_draining_period(ctx->qconn)) {
+ /* When a QUIC server instance is shutting down, it may send us a
+ * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+ * state.
+ * This may be a stopping of the service or it may be that the server
+ * is reloading and a new instance will start serving soon.
+ * In any case, we tear down our socket and start over with a new one.
+ * We re-open the underlying UDP cf right now, but do not start
+ * connecting until called again.
+ */
+ int reconn_delay_ms = 200;
+
+ DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms));
+ Curl_conn_cf_close(cf->next, data);
+ cf_ngtcp2_ctx_clear(ctx);
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ if(!result && *done) {
+ *done = FALSE;
+ ctx->reconnect_at = now;
+ ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
+ Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
+ result = CURLE_OK;
+ }
+ }
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(result) {
+ const char *r_ip;
+ int r_port;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ infof(data, "QUIC connect to %s port %u failed: %s",
+ r_ip, r_port, curl_easy_strerror(result));
+ }
+#endif
+ DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+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: {
+ const ngtcp2_transport_params *rp;
+ DEBUGASSERT(pres1);
+
+ CF_DATA_SAVE(save, cf, data);
+ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
+ if(rp)
+ *pres1 = (rp->initial_max_streams_bidi > INT_MAX)?
+ INT_MAX : (int)rp->initial_max_streams_bidi;
+ else /* not arrived yet? */
+ *pres1 = Curl_multi_max_concurrent_streams(data->multi);
+ DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1));
+ CF_DATA_RESTORE(cf, save);
+ return CURLE_OK;
+ }
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_ngtcp2_destroy,
+ cf_ngtcp2_connect,
+ cf_ngtcp2_close,
+ Curl_cf_def_get_host,
+ cf_ngtcp2_get_select_socks,
+ cf_ngtcp2_data_pending,
+ cf_ngtcp2_send,
+ cf_ngtcp2_recv,
+ cf_ngtcp2_data_event,
+ Curl_cf_def_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,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_ngtcp2_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_ngtcp2_ctx_clear(ctx);
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+ if(result)
+ goto out;
+
+ result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+ if(result)
+ goto out;
+
+ cf->conn = conn;
+ udp_cf->conn = cf->conn;
+ udp_cf->sockindex = cf->sockindex;
+ cf->next = udp_cf;
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ if(udp_cf)
+ Curl_conn_cf_discard(udp_cf, data);
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+ return result;
+}
+
+bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
new file mode 100644
index 0000000000..8813ec9a77
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
@@ -0,0 +1,61 @@
+#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H
+#define HEADER_CURL_VQUIC_CURL_NGTCP2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_NGTCP2
+
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <nghttp3/nghttp3.h>
+#ifdef USE_OPENSSL
+#include <openssl/ssl.h>
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#endif
+
+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 connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+#endif
+
+#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
new file mode 100644
index 0000000000..54408d75a3
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -0,0 +1,1433 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QUICHE
+#include <quiche.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include "urldata.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "strcase.h"
+#include "multiif.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "curl_quiche.h"
+#include "transfer.h"
+#include "h2h3.h"
+#include "vtls/openssl.h"
+#include "vtls/keylog.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
+
+/* how many UDP packets to send max in one call */
+#define MAX_PKT_BURST 10
+#define MAX_UDP_PAYLOAD_SIZE 1452
+
+/*
+ * Store quiche version info in this buffer.
+ */
+void Curl_quiche_ver(char *p, size_t len)
+{
+ (void)msnprintf(p, len, "quiche/%s", quiche_version());
+}
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+
+static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+ SSL_CTX_set_alpn_protos(ssl_ctx,
+ (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ {
+ struct connectdata *conn = data->conn;
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+ if(ssl_cafile || ssl_capath) {
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ /* tell OpenSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return NULL;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use openssl's built-in default as fallback */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ }
+ return ssl_ctx;
+}
+
+struct quic_handshake {
+ char *buf; /* pointer to the buffer */
+ size_t alloclen; /* size of allocation */
+ size_t len; /* size of content in buffer */
+ size_t nread; /* how many bytes have been read */
+};
+
+struct h3_event_node {
+ struct h3_event_node *next;
+ quiche_h3_event *ev;
+};
+
+struct cf_quiche_ctx {
+ struct cf_quic_ctx q;
+ quiche_conn *qconn;
+ quiche_config *cfg;
+ quiche_h3_conn *h3c;
+ quiche_h3_config *h3config;
+ uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
+ SSL_CTX *sslctx;
+ SSL *ssl;
+ struct curltime started_at; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ struct curltime reconnect_at; /* time the next attempt should start */
+ BIT(goaway); /* got GOAWAY from server */
+ BIT(got_first_byte); /* if first byte was received */
+};
+
+
+#ifdef DEBUG_QUICHE
+static void quiche_debug_log(const char *line, void *argp)
+{
+ (void)argp;
+ fprintf(stderr, "%s\n", line);
+}
+#endif
+
+static void h3_clear_pending(struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ if(stream->pending) {
+ struct h3_event_node *node, *next;
+ for(node = stream->pending; node; node = next) {
+ next = node->next;
+ quiche_h3_event_free(node->ev);
+ free(node);
+ }
+ stream->pending = NULL;
+ }
+}
+
+static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
+{
+ if(ctx) {
+ vquic_ctx_free(&ctx->q);
+ if(ctx->qconn)
+ quiche_conn_free(ctx->qconn);
+ if(ctx->h3config)
+ quiche_h3_config_free(ctx->h3config);
+ if(ctx->h3c)
+ quiche_h3_conn_free(ctx->h3c);
+ if(ctx->cfg)
+ quiche_config_free(ctx->cfg);
+ memset(ctx, 0, sizeof(*ctx));
+ }
+}
+
+static void notify_drain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ data->state.drain = 1;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+}
+
+static CURLcode h3_add_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int64_t stream3_id, quiche_h3_event *ev)
+{
+ struct Curl_easy *mdata;
+ struct h3_event_node *node, **pnext;
+
+ DEBUGASSERT(data->multi);
+ for(mdata = data->multi->easyp; mdata; mdata = mdata->next) {
+ if(mdata->req.p.http && mdata->req.p.http->stream3_id == stream3_id) {
+ break;
+ }
+ }
+
+ if(!mdata) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] event discarded, easy handle "
+ "not found", stream3_id));
+ quiche_h3_event_free(ev);
+ return CURLE_OK;
+ }
+
+ node = calloc(sizeof(*node), 1);
+ if(!node) {
+ quiche_h3_event_free(ev);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ node->ev = ev;
+ /* append to process them in order of arrival */
+ pnext = &mdata->req.p.http->pending;
+ while(*pnext) {
+ pnext = &((*pnext)->next);
+ }
+ *pnext = node;
+ notify_drain(cf, mdata);
+ return CURLE_OK;
+}
+
+struct h3h1header {
+ char *dest;
+ size_t destlen; /* left to use */
+ size_t nlen; /* used */
+};
+
+static int cb_each_header(uint8_t *name, size_t name_len,
+ uint8_t *value, size_t value_len,
+ void *argp)
+{
+ struct h3h1header *headers = (struct h3h1header *)argp;
+ size_t olen = 0;
+
+ if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
+ msnprintf(headers->dest,
+ headers->destlen, "HTTP/3 %.*s \r\n",
+ (int) value_len, value);
+ }
+ else if(!headers->nlen) {
+ return CURLE_HTTP3;
+ }
+ else {
+ msnprintf(headers->dest,
+ headers->destlen, "%.*s: %.*s\r\n",
+ (int)name_len, name, (int) value_len, value);
+ }
+ olen = strlen(headers->dest);
+ headers->destlen -= olen;
+ headers->nlen += olen;
+ headers->dest += olen;
+ return 0;
+}
+
+static ssize_t cf_recv_body(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread;
+ size_t offset = 0;
+
+ if(!stream->firstbody) {
+ /* add a header-body separator CRLF */
+ offset = 2;
+ }
+ nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ (unsigned char *)buf + offset, len - offset);
+ if(nread >= 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][DATA] len=%zd",
+ stream->stream3_id, nread));
+ if(!stream->firstbody) {
+ stream->firstbody = TRUE;
+ buf[0] = '\r';
+ buf[1] = '\n';
+ nread += offset;
+ }
+ }
+ else if(nread == -1) {
+ *err = CURLE_AGAIN;
+ stream->h3_recving_data = FALSE;
+ }
+ else {
+ failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]",
+ nread, stream->stream3_id);
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ streamclose(cf->conn, "Reset of stream");
+ stream->h3_recving_data = FALSE;
+ nread = -1;
+ *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ }
+ return nread;
+}
+
+#ifdef DEBUGBUILD
+static const char *cf_ev_name(quiche_h3_event *ev)
+{
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ return "HEADERS";
+ case QUICHE_H3_EVENT_DATA:
+ return "DATA";
+ case QUICHE_H3_EVENT_RESET:
+ return "RESET";
+ case QUICHE_H3_EVENT_FINISHED:
+ return "FINISHED";
+ case QUICHE_H3_EVENT_GOAWAY:
+ return "GOAWAY";
+ default:
+ return "Unknown";
+ }
+}
+#else
+#define cf_ev_name(x) ""
+#endif
+
+static ssize_t h3_process_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ int64_t stream3_id,
+ quiche_h3_event *ev,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = 0;
+ int rc;
+ struct h3h1header headers;
+
+ DEBUGASSERT(stream3_id == stream->stream3_id);
+
+ *err = CURLE_OK;
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ stream->h3_got_header = TRUE;
+ headers.dest = buf;
+ headers.destlen = len;
+ headers.nlen = 0;
+ rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
+ if(rc) {
+ failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]",
+ rc, stream3_id);
+ *err = CURLE_RECV_ERROR;
+ recvd = -1;
+ break;
+ }
+ recvd = headers.nlen;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS] len=%zd",
+ stream3_id, recvd));
+ break;
+
+ case QUICHE_H3_EVENT_DATA:
+ DEBUGASSERT(!stream->closed);
+ stream->h3_recving_data = TRUE;
+ recvd = cf_recv_body(cf, data, buf, len, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ break;
+
+ case QUICHE_H3_EVENT_RESET:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id));
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ /* streamclose(cf->conn, "Reset of stream");*/
+ stream->h3_recving_data = FALSE;
+ break;
+
+ case QUICHE_H3_EVENT_FINISHED:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id));
+ stream->closed = TRUE;
+ /* streamclose(cf->conn, "End of stream");*/
+ stream->h3_recving_data = FALSE;
+ break;
+
+ case QUICHE_H3_EVENT_GOAWAY:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id));
+ break;
+
+ default:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d",
+ stream3_id, quiche_h3_event_type(ev)));
+ break;
+ }
+ return recvd;
+}
+
+static ssize_t h3_process_pending(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ struct h3_event_node *node = stream->pending, **pnext = &stream->pending;
+ ssize_t recvd = 0, erecvd;
+
+ *err = CURLE_OK;
+ DEBUGASSERT(stream);
+ while(node && len) {
+ erecvd = h3_process_event(cf, data, buf, len,
+ stream->stream3_id, node->ev, err);
+ quiche_h3_event_free(node->ev);
+ *pnext = node->next;
+ free(node);
+ node = *pnext;
+ if(erecvd < 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] process event -> %d",
+ stream->stream3_id, *err));
+ return erecvd;
+ }
+ recvd += erecvd;
+ *err = CURLE_OK;
+ buf += erecvd;
+ len -= erecvd;
+ }
+ return recvd;
+}
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
+ uint8_t buf[65536];
+ size_t bufsize = sizeof(buf);
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ quiche_recv_info recv_info;
+ ssize_t recvd, nread;
+ ssize_t total = 0, pkts = 0;
+
+ DEBUGASSERT(ctx->qconn);
+
+ /* in case the timeout expired */
+ quiche_conn_on_timeout(ctx->qconn);
+
+ do {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd < 0) {
+ if((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)) {
+ break;
+ }
+ if(SOCKERRNO == ECONNREFUSED) {
+ const char *r_ip;
+ int r_port;
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ failf(data, "quiche: connection to %s:%u refused",
+ r_ip, r_port);
+ return CURLE_COULDNT_CONNECT;
+ }
+ failf(data, "quiche: recvfrom() unexpectedly returned %zd "
+ "(errno: %d, socket %d)", recvd, SOCKERRNO, ctx->q.sockfd);
+ return CURLE_RECV_ERROR;
+ }
+
+ total += recvd;
+ ++pkts;
+ if(recvd > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+ recv_info.from = (struct sockaddr *) &remote_addr;
+ recv_info.from_len = remote_addrlen;
+ recv_info.to = (struct sockaddr *) &ctx->q.local_addr;
+ recv_info.to_len = ctx->q.local_addrlen;
+
+ nread = quiche_conn_recv(ctx->qconn, buf, recvd, &recv_info);
+ if(nread < 0) {
+ if(QUICHE_ERR_DONE == nread) {
+ DEBUGF(LOG_CF(data, cf, "ingress, quiche is DONE"));
+ return CURLE_OK;
+ }
+ else if(QUICHE_ERR_TLS_FAIL == nread) {
+ long verify_ok = SSL_get_verify_result(ctx->ssl);
+ if(verify_ok != X509_V_OK) {
+ failf(data, "SSL certificate problem: %s",
+ X509_verify_cert_error_string(verify_ok));
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ else {
+ failf(data, "quiche_conn_recv() == %zd", nread);
+ return CURLE_RECV_ERROR;
+ }
+ }
+ else if(nread < recvd) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, quiche only "
+ "accepted %zd/%zd bytes",
+ stream3_id, nread, recvd));
+ }
+
+ } while(pkts < 1000); /* arbitrary */
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, recvd %zd bytes "
+ "in %zd packets", stream3_id, total, pkts));
+ return CURLE_OK;
+}
+
+/*
+ * flush_egress drains the buffers and sends off data.
+ * Calls failf() on errors.
+ */
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
+ quiche_send_info send_info;
+ ssize_t outlen, total_len = 0;
+ size_t max_udp_payload_size =
+ quiche_conn_max_send_udp_payload_size(ctx->qconn);
+ size_t gsolen = max_udp_payload_size;
+ size_t sent, pktcnt = 0;
+ CURLcode result;
+ int64_t timeout_ns;
+
+ ctx->q.no_gso = TRUE;
+ if(ctx->q.num_blocked_pkt) {
+ result = vquic_send_blocked_pkt(cf, data, &ctx->q);
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, still not "
+ "able to send blocked packet", stream3_id));
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ goto out;
+ }
+ }
+
+ for(;;) {
+ outlen = quiche_conn_send(ctx->qconn, ctx->q.pktbuf, max_udp_payload_size,
+ &send_info);
+ if(outlen == QUICHE_ERR_DONE) {
+ result = CURLE_OK;
+ goto out;
+ }
+
+ if(outlen < 0) {
+ failf(data, "quiche_conn_send returned %zd", outlen);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+
+ /* send the pktbuf *before* the last addition */
+ result = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outlen, gsolen, &sent);
+ ++pktcnt;
+ total_len += outlen;
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ /* blocked, add the pktbuf *before* and *at* the last addition
+ * separately to the blocked packages */
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, pushing blocked "
+ "packet with %zd bytes", stream3_id, outlen));
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf, outlen, gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ goto out;
+ }
+ }
+
+out:
+ timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn);
+ if(timeout_ns % 1000000)
+ timeout_ns += 1000000;
+ /* expire resolution is milliseconds */
+ Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC);
+ if(pktcnt)
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, sent %zd packets "
+ "with %zd bytes", stream3_id, pktcnt, total_len));
+ return result;
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+
+ if(stream->reset) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
+ *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
+ stream->stream3_id, *err));
+ goto out;
+ }
+
+ if(!stream->h3_got_header) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->stream3_id);
+ /* *err = CURLE_PARTIAL_FILE; */
+ *err = CURLE_RECV_ERROR;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
+ " -> %d", stream->stream3_id, *err));
+ }
+ *err = CURLE_OK;
+ nread = 0;
+
+out:
+ return nread;
+}
+
+static CURLcode cf_poll_events(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ quiche_h3_event *ev;
+
+ /* Take in the events and distribute them to the transfers. */
+ while(1) {
+ int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev);
+ if(stream3_id < 0) {
+ /* nothing more to do */
+ break;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, queue event %s "
+ "for [h3sid=%"PRId64"]",
+ stream? stream->stream3_id : -1, cf_ev_name(ev),
+ stream3_id));
+ if(h3_add_event(cf, data, stream3_id, ev) != CURLE_OK) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ return CURLE_OK;
+}
+
+static ssize_t cf_recv_transfer_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = -1;
+ size_t offset = 0;
+
+ if(stream->h3_recving_data) {
+ /* try receiving body first */
+ recvd = cf_recv_body(cf, data, buf, len, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ if(recvd > 0) {
+ offset = recvd;
+ }
+ }
+
+ if(offset < len && stream->pending) {
+ /* process any pending events for `data` first. if there are,
+ * return so the transfer can handle those. We do not want to
+ * progress ingress while events are pending here. */
+ recvd = h3_process_pending(cf, data, buf + offset, len - offset, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ if(recvd > 0) {
+ offset += recvd;
+ }
+ }
+
+ if(offset) {
+ *err = CURLE_OK;
+ return offset;
+ }
+ *err = CURLE_AGAIN;
+ return 0;
+}
+
+static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = -1;
+
+ *err = CURLE_AGAIN;
+
+ recvd = cf_recv_transfer_data(cf, data, buf, len, err);
+ if(recvd)
+ goto out;
+ if(stream->closed) {
+ recvd = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+
+ /* we did get nothing from the quiche buffers or pending events.
+ * Take in more data from the connection, any error is fatal */
+ if(cf_process_ingress(cf, data)) {
+ DEBUGF(LOG_CF(data, cf, "h3_stream_recv returns on ingress"));
+ *err = CURLE_RECV_ERROR;
+ recvd = -1;
+ goto out;
+ }
+ /* poll quiche and distribute the events to the transfers */
+ *err = cf_poll_events(cf, data);
+ if(*err) {
+ recvd = -1;
+ goto out;
+ }
+
+ /* try to receive again for this transfer */
+ recvd = cf_recv_transfer_data(cf, data, buf, len, err);
+ if(recvd)
+ goto out;
+ if(stream->closed) {
+ recvd = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+ recvd = -1;
+ *err = CURLE_AGAIN;
+ data->state.drain = 0;
+
+out:
+ if(cf_flush_egress(cf, data)) {
+ DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed"));
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv -> %zd, err=%d",
+ stream->stream3_id, recvd, *err));
+ if(recvd > 0)
+ notify_drain(cf, data);
+ return recvd;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode cf_http_request(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ int64_t stream3_id;
+ quiche_h3_header *nva = NULL;
+ CURLcode result = CURLE_OK;
+ struct h2h3req *hreq = NULL;
+
+ stream->h3req = TRUE; /* send off! */
+ stream->closed = FALSE;
+ stream->reset = FALSE;
+
+ result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(quiche_h3_header) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].name_len = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].value_len = hreq->header[i].valuelen;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ 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, but not zero */
+
+ stream->upload_done = !stream->upload_left;
+ stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
+ stream->upload_done);
+ break;
+ default:
+ stream->upload_left = 0;
+ stream->upload_done = TRUE;
+ stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
+ TRUE);
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ if(stream3_id < 0) {
+ if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
+ DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) rejected "
+ "with H3_ERR_STREAM_BLOCKED",
+ data->state.url, (long)stream->upload_left));
+ result = CURLE_AGAIN;
+ goto fail;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) -> %" PRId64,
+ data->state.url, (long)stream->upload_left, stream3_id));
+ }
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
+ stream3_id, (void *)data);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
+ stream3_id, data->state.url));
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+
+static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nwritten;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) start",
+ stream->h3req? stream->stream3_id : -1, len));
+ *err = cf_process_ingress(cf, data);
+ if(*err)
+ return -1;
+
+ if(!stream->h3req) {
+ CURLcode result = cf_http_request(cf, data, buf, len);
+ if(result) {
+ *err = result;
+ return -1;
+ }
+ nwritten = len;
+ }
+ else {
+ nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ (uint8_t *)buf, len, FALSE);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu) -> %zd",
+ stream->stream3_id, len, nwritten));
+ if(nwritten == QUICHE_H3_ERR_DONE) {
+ /* no error, nothing to do (flow control?) */
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ }
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
+ DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> exceeds size", len));
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ }
+ else if(nwritten < 0) {
+ DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> SEND_ERROR", len));
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ }
+ else {
+ *err = CURLE_OK;
+ }
+ }
+
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ return nwritten;
+}
+
+static bool stream_is_writeable(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+
+ /* surely, there must be a better way */
+ quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn);
+ if(qiter) {
+ uint64_t stream_id;
+ while(quiche_stream_iter_next(qiter, &stream_id)) {
+ if(stream_id == (uint64_t)stream->stream3_id)
+ return TRUE;
+ }
+ quiche_stream_iter_free(qiter);
+ }
+ return FALSE;
+}
+
+static int cf_quiche_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
+ int rv = GETSOCK_BLANK;
+
+ socks[0] = ctx->q.sockfd;
+
+ /* in an HTTP/3 connection we can basically always get a frame so we should
+ always be ready for one */
+ rv |= GETSOCK_READSOCK(0);
+
+ /* we're still uploading or the HTTP/3 layer wants to send data */
+ if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+ && stream_is_writeable(cf, data))
+ rv |= GETSOCK_WRITESOCK(0);
+
+ return rv;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ if(stream->pending) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] has event pending", stream->stream3_id));
+ return TRUE;
+ }
+ if(stream->h3_recving_data) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] is receiving DATA", stream->stream3_id));
+ return TRUE;
+ }
+ if(data->state.drain) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] is draining", stream->stream3_id));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_DONE: {
+ struct HTTP *stream = data->req.p.http;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is %s",
+ stream->stream3_id, arg1? "cancelled" : "done"));
+ h3_clear_pending(data);
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ struct HTTP *stream = data->req.p.http;
+ ssize_t sent;
+ stream->upload_done = TRUE;
+ sent = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ NULL, 0, TRUE);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] send_body FINISHED",
+ stream->stream3_id));
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+ break;
+ }
+ case CF_CTRL_DATA_IDLE:
+ /* anything to do? */
+ break;
+ case CF_CTRL_CONN_REPORT_STATS:
+ if(cf->sockindex == FIRSTSOCKET) {
+ if(ctx->got_first_byte)
+ Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at);
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
+ }
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ if(cf->conn->ssl_config.verifyhost) {
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(ctx->ssl);
+ if(!server_cert) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto out;
+ }
+ result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ goto out;
+ }
+ else
+ DEBUGF(LOG_CF(data, cf, "Skipped certificate verification"));
+
+ ctx->h3config = quiche_h3_config_new();
+ if(!ctx->h3config) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Create a new HTTP/3 connection on the QUIC connection. */
+ ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
+ if(!ctx->h3c) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, ctx->ssl);
+
+out:
+ if(result) {
+ if(ctx->h3config) {
+ quiche_h3_config_free(ctx->h3config);
+ ctx->h3config = NULL;
+ }
+ if(ctx->h3c) {
+ quiche_h3_conn_free(ctx->h3c);
+ ctx->h3c = NULL;
+ }
+ }
+ return result;
+}
+
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int rv;
+ CURLcode result;
+ const struct Curl_sockaddr_ex *sockaddr;
+
+ DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
+
+#ifdef DEBUG_QUICHE
+ /* initialize debug log callback only once */
+ static int debug_log_init = 0;
+ if(!debug_log_init) {
+ quiche_enable_debug_logging(quiche_debug_log, NULL);
+ debug_log_init = 1;
+ }
+#endif
+
+ result = vquic_ctx_init(&ctx->q, MAX_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
+ if(result)
+ return result;
+
+ ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
+ if(!ctx->cfg) {
+ failf(data, "can't create quiche config");
+ return CURLE_FAILED_INIT;
+ }
+ quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT);
+ quiche_config_set_initial_max_data(ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_local(
+ ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_remote(
+ ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_uni(ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_application_protos(ctx->cfg,
+ (uint8_t *)
+ QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
+ - 1);
+
+ DEBUGASSERT(!ctx->ssl);
+ DEBUGASSERT(!ctx->sslctx);
+ ctx->sslctx = quic_ssl_ctx(data);
+ if(!ctx->sslctx)
+ return CURLE_QUIC_CONNECT_ERROR;
+ ctx->ssl = SSL_new(ctx->sslctx);
+ if(!ctx->ssl)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ SSL_set_app_data(ctx->ssl, cf);
+ SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+
+ result = Curl_rand(data, ctx->scid, sizeof(ctx->scid));
+ if(result)
+ return result;
+
+ Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+ &sockaddr, NULL, NULL, NULL, NULL);
+ 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;
+
+ ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid,
+ sizeof(ctx->scid), NULL, 0,
+ (struct sockaddr *)&ctx->q.local_addr,
+ ctx->q.local_addrlen,
+ &sockaddr->sa_addr, sockaddr->addrlen,
+ ctx->cfg, ctx->ssl, false);
+ if(!ctx->qconn) {
+ failf(data, "can't create quiche connection");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Known to not work on Windows */
+#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
+ {
+ int qfd;
+ (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd);
+ if(qfd != -1)
+ quiche_conn_set_qlog_fd(ctx->qconn, qfd,
+ "qlog title", "curl qlog");
+ }
+#endif
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ return result;
+
+ {
+ unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
+ unsigned alpn_len, offset = 0;
+
+ /* Replace each ALPN length prefix by a comma. */
+ while(offset < sizeof(alpn_protocols) - 1) {
+ alpn_len = alpn_protocols[offset];
+ alpn_protocols[offset] = ',';
+ offset += 1 + alpn_len;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
+ alpn_protocols + 1));
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct curltime now;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect the UDP filter first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+
+ if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+ /* Not time yet to attempt the next connect */
+ DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ goto out;
+ }
+
+ if(!ctx->qconn) {
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ ctx->started_at = now;
+ result = cf_flush_egress(cf, data);
+ /* we do not expect to be able to recv anything yet */
+ goto out;
+ }
+
+ result = cf_process_ingress(cf, data);
+ if(result)
+ goto out;
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ goto out;
+
+ if(quiche_conn_is_established(ctx->qconn)) {
+ DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at)));
+ ctx->handshake_at = now;
+ result = cf_verify_peer(cf, data);
+ if(!result) {
+ DEBUGF(LOG_CF(data, cf, "peer verified"));
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ }
+ }
+ else if(quiche_conn_is_draining(ctx->qconn)) {
+ /* When a QUIC server instance is shutting down, it may send us a
+ * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+ * state.
+ * This may be a stopping of the service or it may be that the server
+ * is reloading and a new instance will start serving soon.
+ * In any case, we tear down our socket and start over with a new one.
+ * We re-open the underlying UDP cf right now, but do not start
+ * connecting until called again.
+ */
+ int reconn_delay_ms = 200;
+
+ DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms));
+ Curl_conn_cf_close(cf->next, data);
+ cf_quiche_ctx_clear(ctx);
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ if(!result && *done) {
+ *done = FALSE;
+ ctx->reconnect_at = Curl_now();
+ ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
+ Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
+ result = CURLE_OK;
+ }
+ }
+
+out:
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(result && result != CURLE_AGAIN) {
+ const char *r_ip;
+ int r_port;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ infof(data, "connect to %s port %u failed: %s",
+ r_ip, r_port, curl_easy_strerror(result));
+ }
+#endif
+ return result;
+}
+
+static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ (void)data;
+ if(ctx) {
+ if(ctx->qconn) {
+ (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
+ /* flushing the egress is not a failsafe way to deliver all the
+ outstanding packets, but we also don't want to get stuck here... */
+ (void)cf_flush_egress(cf, data);
+ }
+ cf_quiche_ctx_clear(ctx);
+ }
+}
+
+static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ (void)data;
+ cf_quiche_ctx_clear(ctx);
+ free(ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ uint64_t max_streams = CONN_INUSE(cf->conn);
+ if(!ctx->goaway) {
+ max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
+ }
+ *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
+ DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1));
+ return CURLE_OK;
+ }
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_quiche_destroy,
+ cf_quiche_connect,
+ cf_quiche_close,
+ Curl_cf_def_get_host,
+ cf_quiche_get_select_socks,
+ cf_quiche_data_pending,
+ cf_quiche_send,
+ cf_quiche_recv,
+ cf_quiche_data_event,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_quiche_query,
+};
+
+CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_quiche_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+ if(result)
+ goto out;
+
+ result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+ if(result)
+ goto out;
+
+ udp_cf->conn = cf->conn;
+ udp_cf->sockindex = cf->sockindex;
+ cf->next = udp_cf;
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ if(udp_cf)
+ Curl_conn_cf_discard(udp_cf, data);
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+bool Curl_conn_is_quiche(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/Utilities/cmcurl/lib/vquic/quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h
index 2da65f5f48..bce781c1bc 100644
--- a/Utilities/cmcurl/lib/vquic/quiche.h
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_QUICHE_H
-#define HEADER_CURL_VQUIC_QUICHE_H
+#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H
+#define HEADER_CURL_VQUIC_CURL_QUICHE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,28 +31,20 @@
#include <quiche.h>
#include <openssl/ssl.h>
-struct quic_handshake {
- char *buf; /* pointer to the buffer */
- size_t alloclen; /* size of allocation */
- size_t len; /* size of content in buffer */
- size_t nread; /* how many bytes have been read */
-};
-
-struct quicsocket {
- quiche_config *cfg;
- quiche_conn *conn;
- quiche_h3_conn *h3c;
- quiche_h3_config *h3config;
- uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
- curl_socket_t sockfd;
- uint32_t version;
- SSL_CTX *sslctx;
- SSL *ssl;
- bool h3_recving; /* TRUE when in h3-body-reading state */
- struct sockaddr_storage local_addr;
- socklen_t local_addrlen;
-};
+struct Curl_cfilter;
+struct Curl_easy;
+
+void Curl_quiche_ver(char *p, size_t len);
+
+CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_quiche(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
#endif
-#endif /* HEADER_CURL_VQUIC_QUICHE_H */
+#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */
diff --git a/Utilities/cmcurl/lib/vquic/msh3.c b/Utilities/cmcurl/lib/vquic/msh3.c
deleted file mode 100644
index c3e58e726a..0000000000
--- a/Utilities/cmcurl/lib/vquic/msh3.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_MSH3
-
-#include "urldata.h"
-#include "timeval.h"
-#include "multiif.h"
-#include "sendf.h"
-#include "connect.h"
-#include "h2h3.h"
-#include "msh3.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* #define DEBUG_HTTP3 1 */
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define MSH3_REQ_INIT_BUF_LEN 8192
-
-static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
-static int msh3_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks);
-static CURLcode msh3_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection);
-static unsigned int msh3_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform);
-static Curl_recv msh3_stream_recv;
-static Curl_send msh3_stream_send;
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *IfContext,
- const MSH3_HEADER *Header);
-static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t Length,
- const uint8_t *Data);
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool Aborted, uint64_t AbortError);
-static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
-
-static const struct Curl_handler msh3_curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- msh3_do_it, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- msh3_getsock, /* proto_getsock */
- msh3_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- msh3_getsock, /* perform_getsock */
- msh3_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- msh3_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-static const MSH3_REQUEST_IF msh3_request_if = {
- msh3_header_received,
- msh3_data_received,
- msh3_complete,
- msh3_shutdown
-};
-
-void Curl_quic_ver(char *p, size_t len)
-{
- uint32_t v[4];
- MsH3Version(v);
- (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
-}
-
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct quicsocket *qs = &conn->hequic[sockindex];
- bool insecure = !conn->ssl_config.verifypeer;
- memset(qs, 0, sizeof(*qs));
-
- (void)sockfd;
- (void)addr; /* TODO - Pass address along */
- (void)addrlen;
-
- H3BUGF(infof(data, "creating new api/connection"));
-
- qs->api = MsH3ApiOpen();
- if(!qs->api) {
- failf(data, "can't create msh3 api");
- return CURLE_FAILED_INIT;
- }
-
- qs->conn = MsH3ConnectionOpen(qs->api,
- conn->host.name,
- (uint16_t)conn->remote_port,
- insecure);
- if(!qs->conn) {
- failf(data, "can't create msh3 connection");
- if(qs->api) {
- MsH3ApiClose(qs->api);
- }
- return CURLE_FAILED_INIT;
- }
-
- return CURLE_OK;
-}
-
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected)
-{
- struct quicsocket *qs = &conn->hequic[sockindex];
- MSH3_CONNECTION_STATE state;
-
- state = MsH3ConnectionGetState(qs->conn, false);
- if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
- failf(data, "failed to connect, state=%u", (uint32_t)state);
- return CURLE_COULDNT_CONNECT;
- }
-
- if(state == MSH3_CONN_CONNECTED) {
- H3BUGF(infof(data, "connection connected"));
- *connected = true;
- conn->quic = qs;
- conn->recv[sockindex] = msh3_stream_recv;
- conn->send[sockindex] = msh3_stream_send;
- conn->handler = &msh3_curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
- /* TODO - Clean up other happy-eyeballs connection(s)? */
- }
-
- return CURLE_OK;
-}
-
-static int msh3_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks)
-{
- struct HTTP *stream = data->req.p.http;
- int bitmap = GETSOCK_BLANK;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- if(stream->recv_error) {
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
- data->state.drain++;
- }
- else if(stream->recv_header_len || stream->recv_data_len) {
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
- data->state.drain++;
- }
-
- H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
-
- return bitmap;
-}
-
-static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
-{
- struct HTTP *stream = data->req.p.http;
- H3BUGF(infof(data, "msh3_do_it"));
- stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
- if(!stream->recv_buf) {
- return CURLE_OUT_OF_MEMORY;
- }
- stream->req = ZERO_NULL;
- msh3_lock_initialize(&stream->recv_lock);
- stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
- stream->recv_header_len = 0;
- stream->recv_header_complete = false;
- stream->recv_data_len = 0;
- stream->recv_data_complete = false;
- stream->recv_error = CURLE_OK;
- return Curl_http(data, done);
-}
-
-static unsigned int msh3_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- H3BUGF(infof(data, "msh3_conncheck"));
- return CONNRESULT_NONE;
-}
-
-static void disconnect(struct quicsocket *qs)
-{
- if(qs->conn) {
- MsH3ConnectionClose(qs->conn);
- qs->conn = ZERO_NULL;
- }
- if(qs->api) {
- MsH3ApiClose(qs->api);
- qs->api = ZERO_NULL;
- }
-}
-
-static CURLcode msh3_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead_connection)
-{
- (void)data;
- (void)dead_connection;
- H3BUGF(infof(data, "disconnecting (msh3)"));
- disconnect(conn->quic);
- return CURLE_OK;
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
- int tempindex)
-{
- (void)data;
- if(conn->transport == TRNSPRT_QUIC) {
- H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
- disconnect(&conn->hequic[tempindex]);
- }
-}
-
-/* Requires stream->recv_lock to be held */
-static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
-{
- uint8_t *new_recv_buf;
- const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
- if(cur_recv_len + len > stream->recv_buf_alloc) {
- size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
- do {
- new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
- } while(cur_recv_len + len > new_recv_buf_alloc_len);
- new_recv_buf = malloc(new_recv_buf_alloc_len);
- if(!new_recv_buf) {
- return false;
- }
- if(cur_recv_len) {
- memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
- }
- stream->recv_buf_alloc = new_recv_buf_alloc_len;
- free(stream->recv_buf);
- stream->recv_buf = new_recv_buf;
- }
- return true;
-}
-
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *IfContext,
- const MSH3_HEADER *Header)
-{
- struct HTTP *stream = IfContext;
- size_t total_len;
- (void)Request;
-
- if(stream->recv_header_complete) {
- H3BUGF(printf("* ignoring header after data\n"));
- return;
- }
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if((Header->NameLength == 7) &&
- !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
- total_len = 9 + Header->ValueLength;
- if(!msh3request_ensure_room(stream, total_len)) {
- /* TODO - handle error */
- goto release_lock;
- }
- msnprintf((char *)stream->recv_buf + stream->recv_header_len,
- stream->recv_buf_alloc - stream->recv_header_len,
- "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
- }
- else {
- total_len = Header->NameLength + 4 + Header->ValueLength;
- if(!msh3request_ensure_room(stream, total_len)) {
- /* TODO - handle error */
- goto release_lock;
- }
- msnprintf((char *)stream->recv_buf + stream->recv_header_len,
- stream->recv_buf_alloc - stream->recv_header_len,
- "%.*s: %.*s\n",
- (int)Header->NameLength, Header->Name,
- (int)Header->ValueLength, Header->Value);
- }
-
- stream->recv_header_len += total_len - 1; /* don't include null-terminator */
-
-release_lock:
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t Length,
- const uint8_t *Data)
-{
- struct HTTP *stream = IfContext;
- size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
- (void)Request;
- H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
- Length, cur_recv_len, stream->recv_buf_alloc));
- msh3_lock_acquire(&stream->recv_lock);
- if(!stream->recv_header_complete) {
- H3BUGF(printf("* Headers complete!\n"));
- if(!msh3request_ensure_room(stream, 2)) {
- /* TODO - handle error */
- goto release_lock;
- }
- stream->recv_buf[stream->recv_header_len++] = '\r';
- stream->recv_buf[stream->recv_header_len++] = '\n';
- stream->recv_header_complete = true;
- cur_recv_len += 2;
- }
- if(!msh3request_ensure_room(stream, Length)) {
- /* TODO - handle error */
- goto release_lock;
- }
- memcpy(stream->recv_buf + cur_recv_len, Data, Length);
- stream->recv_data_len += (size_t)Length;
-release_lock:
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool Aborted, uint64_t AbortError)
-{
- struct HTTP *stream = IfContext;
- (void)Request;
- (void)AbortError;
- H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
- msh3_lock_acquire(&stream->recv_lock);
- if(Aborted) {
- stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
- }
- stream->recv_header_complete = true;
- stream->recv_data_complete = true;
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
-{
- struct HTTP *stream = IfContext;
- (void)Request;
- (void)stream;
-}
-
-static ssize_t msh3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- struct h2h3req *hreq;
-
- (void)sockindex;
- /* Sizes must match for cast below to work" */
- DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
-
- H3BUGF(infof(data, "msh3_stream_send %zu", len));
-
- if(!stream->req) {
- *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
- if(*curlcode) {
- failf(data, "Curl_pseudo_headers failed");
- return -1;
- }
- H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
- stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
- (MSH3_HEADER*)hreq->header, hreq->entries);
- Curl_pseudo_free(hreq);
- if(!stream->req) {
- failf(data, "request open failed");
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- *curlcode = CURLE_OK;
- return len;
- }
- H3BUGF(infof(data, "send %zd body bytes on request %p", len,
- (void *)stream->req));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
-}
-
-static ssize_t msh3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- struct HTTP *stream = data->req.p.http;
- size_t outsize = 0;
- (void)sockindex;
- H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
-
- if(stream->recv_error) {
- failf(data, "request aborted");
- *curlcode = stream->recv_error;
- return -1;
- }
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if(stream->recv_header_len) {
- outsize = buffersize;
- if(stream->recv_header_len < outsize) {
- outsize = stream->recv_header_len;
- }
- memcpy(buf, stream->recv_buf, outsize);
- if(outsize < stream->recv_header_len + stream->recv_data_len) {
- memmove(stream->recv_buf, stream->recv_buf + outsize,
- stream->recv_header_len + stream->recv_data_len - outsize);
- }
- stream->recv_header_len -= outsize;
- H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
- }
- else if(stream->recv_data_len) {
- outsize = buffersize;
- if(stream->recv_data_len < outsize) {
- outsize = stream->recv_data_len;
- }
- memcpy(buf, stream->recv_buf, outsize);
- if(outsize < stream->recv_data_len) {
- memmove(stream->recv_buf, stream->recv_buf + outsize,
- stream->recv_data_len - outsize);
- }
- stream->recv_data_len -= outsize;
- H3BUGF(infof(data, "returned %zu bytes of data", outsize));
- }
- else if(stream->recv_data_complete) {
- H3BUGF(infof(data, "receive complete"));
- }
-
- msh3_lock_release(&stream->recv_lock);
-
- return (ssize_t)outsize;
-}
-
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- H3BUGF(infof(data, "Curl_quic_done_sending"));
- if(conn->handler == &msh3_curl_handler_http3) {
- struct HTTP *stream = data->req.p.http;
- stream->upload_done = TRUE;
- }
-
- return CURLE_OK;
-}
-
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- struct HTTP *stream = data->req.p.http;
- (void)premature;
- H3BUGF(infof(data, "Curl_quic_done"));
- if(stream) {
- if(stream->recv_buf) {
- Curl_safefree(stream->recv_buf);
- msh3_lock_uninitialize(&stream->recv_lock);
- }
- if(stream->req) {
- MsH3RequestClose(stream->req);
- stream->req = ZERO_NULL;
- }
- }
-}
-
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- struct HTTP *stream = data->req.p.http;
- H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
- return stream->recv_header_len || stream->recv_data_len;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- (void)data;
- H3BUGF(infof(data, "Curl_quic_idle"));
- return CURLE_OK;
-}
-
-#endif /* USE_MSH3 */
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c
deleted file mode 100644
index f16b469946..0000000000
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.c
+++ /dev/null
@@ -1,2266 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_NGTCP2
-#include <ngtcp2/ngtcp2.h>
-#include <nghttp3/nghttp3.h>
-
-#ifdef USE_OPENSSL
-#include <openssl/err.h>
-#ifdef OPENSSL_IS_BORINGSSL
-#include <ngtcp2/ngtcp2_crypto_boringssl.h>
-#else
-#include <ngtcp2/ngtcp2_crypto_openssl.h>
-#endif
-#include "vtls/openssl.h"
-#elif defined(USE_GNUTLS)
-#include <ngtcp2/ngtcp2_crypto_gnutls.h>
-#include "vtls/gtls.h"
-#elif defined(USE_WOLFSSL)
-#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "ngtcp2.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "vquic.h"
-#include "h2h3.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* #define DEBUG_NGTCP2 */
-#ifdef CURLDEBUG
-#define DEBUG_HTTP3
-#endif
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define H3_ALPN_H3_29 "\x5h3-29"
-#define H3_ALPN_H3 "\x2h3"
-
-/*
- * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
- * It is used as a circular buffer. Add new bytes at the end until it reaches
- * the far end, then start over at index 0 again.
- */
-
-#define H3_SEND_SIZE (256*1024)
-struct h3out {
- uint8_t buf[H3_SEND_SIZE];
- size_t used; /* number of bytes used in the buffer */
- size_t windex; /* index in the buffer where to start writing the next
- data block */
-};
-
-#define QUIC_MAX_STREAMS (256*1024)
-#define QUIC_MAX_DATA (1*1024*1024)
-#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
-
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-/* ngtcp2 default congestion controller does not perform pacing. Limit
- the maximum packet burst to MAX_PKT_BURST packets. */
-#define MAX_PKT_BURST 10
-
-static CURLcode ng_process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs);
-static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs);
-static int cb_h3_acked_stream_data(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 quicsocket *qs = conn_ref->user_data;
- return qs->qconn;
-}
-
-static ngtcp2_tstamp timestamp(void)
-{
- struct curltime ct = Curl_now();
- return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
-}
-
-#ifdef DEBUG_NGTCP2
-static void quic_printf(void *user_data, const char *fmt, ...)
-{
- va_list ap;
- (void)user_data; /* TODO, use this to do infof() instead long-term */
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-#endif
-
-static void qlog_callback(void *user_data, uint32_t flags,
- const void *data, size_t datalen)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- (void)flags;
- if(qs->qlogfd != -1) {
- ssize_t rc = write(qs->qlogfd, data, datalen);
- if(rc == -1) {
- /* on write error, stop further write attempts */
- close(qs->qlogfd);
- qs->qlogfd = -1;
- }
- }
-
-}
-
-static void quic_settings(struct quicsocket *qs,
- uint64_t stream_buffer_size)
-{
- ngtcp2_settings *s = &qs->settings;
- ngtcp2_transport_params *t = &qs->transport_params;
- ngtcp2_settings_default(s);
- ngtcp2_transport_params_default(t);
-#ifdef DEBUG_NGTCP2
- s->log_printf = quic_printf;
-#else
- s->log_printf = NULL;
-#endif
- s->initial_ts = timestamp();
- t->initial_max_stream_data_bidi_local = stream_buffer_size;
- t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
- t->initial_max_stream_data_uni = QUIC_MAX_STREAMS;
- t->initial_max_data = QUIC_MAX_DATA;
- t->initial_max_streams_bidi = 1;
- t->initial_max_streams_uni = 3;
- t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
- if(qs->qlogfd != -1) {
- s->qlog.write = qlog_callback;
- }
-}
-
-#ifdef USE_OPENSSL
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
- const gnutls_datum_t *secret)
-{
- gnutls_datum_t crandom;
- gnutls_datum_t srandom;
-
- gnutls_session_get_random(session, &crandom, &srandom);
- if(crandom.size != 32) {
- return -1;
- }
-
- Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
- return 0;
-}
-#elif defined(USE_WOLFSSL)
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#endif
-#endif
-
-static int init_ngh3_conn(struct quicsocket *qs);
-
-#ifdef USE_OPENSSL
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
-#ifdef OPENSSL_IS_BORINGSSL
- if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
- return NULL;
- }
-#else
- if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
- return NULL;
- }
-#endif
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
-#ifdef OPENSSL_IS_BORINGSSL
- if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_curves_list failed");
- return NULL;
- }
-#else
- if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
- return NULL;
- }
-
- if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_groups_list failed");
- return NULL;
- }
-#endif
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
- }
-
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
-
- if(ssl_cafile || ssl_capath) {
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell OpenSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- return ssl_ctx;
-}
-
-static CURLcode quic_set_client_cert(struct Curl_easy *data,
- struct quicsocket *qs)
-{
- SSL_CTX *ssl_ctx = qs->sslctx;
- const struct ssl_config_data *ssl_config;
-
- ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
- DEBUGASSERT(ssl_config);
-
- if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
- || ssl_config->cert_type) {
- return Curl_ossl_set_client_cert(
- data, ssl_ctx, ssl_config->primary.clientcert,
- ssl_config->primary.cert_blob, ssl_config->cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd);
- }
-
- return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
-
- (void)data;
- (void)conn;
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- SSL_set_app_data(qs->ssl, &qs->conn_ref);
- SSL_set_connect_state(qs->ssl);
- SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- SSL_set_tlsext_host_name(qs->ssl, hostname);
- return CURLE_OK;
-}
-#elif defined(USE_GNUTLS)
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- CURLcode result;
- gnutls_datum_t alpn[2];
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
- long * const pverifyresult = &data->set.ssl.certverifyresult;
- int rc;
-
- DEBUGASSERT(qs->gtls == NULL);
- qs->gtls = calloc(1, sizeof(*(qs->gtls)));
- if(!qs->gtls)
- return CURLE_OUT_OF_MEMORY;
-
- result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl,
- hostname, qs->gtls, pverifyresult);
- if(result)
- return result;
-
- gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref);
-
- if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) {
- H3BUGF(fprintf(stderr,
- "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL);
- if(rc < 0) {
- H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
- gnutls_strerror(rc)));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(qs->gtls->session, keylog_callback);
- }
-
- /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
- alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
- alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
- alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
- alpn[1].size = sizeof(H3_ALPN_H3) - 2;
-
- gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY);
-
- return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
-
-static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
-
- if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
- return NULL;
- }
-
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
-
- if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
- return NULL;
- }
-
- if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_groups_list failed");
- return NULL;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-#else
- failf(data, "wolfSSL was built without keylog callback");
- return NULL;
-#endif
- }
-
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
-
- if(ssl_cafile || ssl_capath) {
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell wolfSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use wolfssl's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- else {
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
- }
-
- return ssl_ctx;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
-
- (void)data;
- (void)conn;
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- wolfSSL_set_app_data(qs->ssl, &qs->conn_ref);
- wolfSSL_set_connect_state(qs->ssl);
- wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME,
- hostname, (unsigned short)strlen(hostname));
-
- return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
-static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
-{
- (void)user_data;
- (void)tconn;
- return 0;
-}
-
-static void extend_stream_window(ngtcp2_conn *tconn,
- struct HTTP *stream)
-{
- size_t thismuch = stream->unacked_window;
- ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch);
- ngtcp2_conn_extend_max_offset(tconn, thismuch);
- stream->unacked_window = 0;
-}
-
-
-static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
- int64_t stream_id, uint64_t offset,
- const uint8_t *buf, size_t buflen,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- nghttp3_ssize nconsumed;
- int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
- (void)offset;
- (void)stream_user_data;
-
- nconsumed =
- nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
- if(nconsumed < 0) {
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed),
- NULL, 0);
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- /* number of bytes inside buflen which consists of framing overhead
- * including QPACK HEADERS. In other words, it does not consume payload of
- * DATA frame. */
- ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
- ngtcp2_conn_extend_max_offset(tconn, nconsumed);
-
- return 0;
-}
-
-static int
-cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t offset, uint64_t datalen, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)stream_id;
- (void)tconn;
- (void)offset;
- (void)datalen;
- (void)stream_user_data;
-
- rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
- int64_t stream_id, uint64_t app_error_code,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)stream_user_data;
- /* stream is closed... */
-
- if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
- app_error_code = NGHTTP3_H3_NO_ERROR;
- }
-
- rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
- app_error_code);
- if(rv) {
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t final_size, uint64_t app_error_code,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)final_size;
- (void)app_error_code;
- (void)stream_user_data;
-
- rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)app_error_code;
- (void)stream_user_data;
-
- rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
- uint64_t max_streams,
- void *user_data)
-{
- (void)tconn;
- (void)max_streams;
- (void)user_data;
-
- return 0;
-}
-
-static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t max_data, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)max_data;
- (void)stream_user_data;
-
- rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static void cb_rand(uint8_t *dest, size_t destlen,
- const ngtcp2_rand_ctx *rand_ctx)
-{
- CURLcode result;
- (void)rand_ctx;
-
- result = Curl_rand(NULL, dest, destlen);
- if(result) {
- /* cb_rand is only used for non-cryptographic context. If Curl_rand
- failed, just fill 0 and call it *random*. */
- memset(dest, 0, destlen);
- }
-}
-
-static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
- uint8_t *token, size_t cidlen,
- void *user_data)
-{
- CURLcode result;
- (void)tconn;
- (void)user_data;
-
- result = Curl_rand(NULL, cid->data, cidlen);
- if(result)
- return NGTCP2_ERR_CALLBACK_FAILURE;
- cid->datalen = cidlen;
-
- result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
- if(result)
- return NGTCP2_ERR_CALLBACK_FAILURE;
-
- return 0;
-}
-
-static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
- void *user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- (void)tconn;
-
- if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
- return 0;
- }
-
- if(init_ngh3_conn(qs) != CURLE_OK) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static ngtcp2_callbacks ng_callbacks = {
- ngtcp2_crypto_client_initial_cb,
- NULL, /* recv_client_initial */
- ngtcp2_crypto_recv_crypto_data_cb,
- cb_handshake_completed,
- NULL, /* recv_version_negotiation */
- ngtcp2_crypto_encrypt_cb,
- ngtcp2_crypto_decrypt_cb,
- ngtcp2_crypto_hp_mask_cb,
- cb_recv_stream_data,
- cb_acked_stream_data_offset,
- NULL, /* stream_open */
- cb_stream_close,
- NULL, /* recv_stateless_reset */
- ngtcp2_crypto_recv_retry_cb,
- cb_extend_max_local_streams_bidi,
- NULL, /* extend_max_local_streams_uni */
- cb_rand,
- cb_get_new_connection_id,
- NULL, /* remove_connection_id */
- ngtcp2_crypto_update_key_cb, /* update_key */
- NULL, /* path_validation */
- NULL, /* select_preferred_addr */
- cb_stream_reset,
- NULL, /* extend_max_remote_streams_bidi */
- NULL, /* extend_max_remote_streams_uni */
- cb_extend_max_stream_data,
- NULL, /* dcid_status */
- NULL, /* handshake_confirmed */
- NULL, /* recv_new_token */
- ngtcp2_crypto_delete_crypto_aead_ctx_cb,
- ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
- NULL, /* recv_datagram */
- NULL, /* ack_datagram */
- NULL, /* lost_datagram */
- ngtcp2_crypto_get_path_challenge_data_cb,
- cb_stream_stop_sending,
- NULL, /* version_negotiation */
- cb_recv_rx_key,
- NULL, /* recv_tx_key */
- NULL, /* early_data_rejected */
-};
-
-/*
- * Might be called twice for happy eyeballs.
- */
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- int rc;
- int rv;
- CURLcode result;
- ngtcp2_path path; /* TODO: this must be initialized properly */
- struct quicsocket *qs = &conn->hequic[sockindex];
- char ipbuf[40];
- int port;
- int qfd;
-
- if(qs->conn)
- Curl_quic_disconnect(data, conn, sockindex);
- qs->conn = conn;
-
- /* extract the used address as a string */
- if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
- char buffer[STRERROR_LEN];
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- infof(data, "Connect socket %d over QUIC to %s:%d",
- sockfd, ipbuf, port);
-
- qs->version = NGTCP2_PROTO_VER_MAX;
-#ifdef USE_OPENSSL
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-
- result = quic_set_client_cert(data, qs);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-#endif
-
- result = quic_init_ssl(qs, data, conn);
- if(result)
- return result;
-
- qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
- result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
- if(result)
- return result;
-
- qs->scid.datalen = NGTCP2_MAX_CIDLEN;
- result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
- if(result)
- return result;
-
- (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
- qs->qlogfd = qfd; /* -1 if failure above */
- quic_settings(qs, data->set.buffer_size);
-
- qs->local_addrlen = sizeof(qs->local_addr);
- rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
- &qs->local_addrlen);
- if(rv == -1)
- return CURLE_QUIC_CONNECT_ERROR;
-
- ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen);
- ngtcp2_addr_init(&path.remote, addr, addrlen);
-
- rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path,
- NGTCP2_PROTO_VER_V1, &ng_callbacks,
- &qs->settings, &qs->transport_params, NULL, qs);
- if(rc)
- return CURLE_QUIC_CONNECT_ERROR;
-
-#ifdef USE_GNUTLS
- ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session);
-#else
- ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
-#endif
-
- ngtcp2_connection_close_error_default(&qs->last_error);
-
-#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
- qs->no_gso = FALSE;
-#else
- qs->no_gso = TRUE;
-#endif
-
- qs->num_blocked_pkt = 0;
- qs->num_blocked_pkt_sent = 0;
- memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt));
-
- qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST;
- qs->pktbuf = malloc(qs->pktbuflen);
- if(!qs->pktbuf) {
- ngtcp2_conn_del(qs->qconn);
- qs->qconn = NULL;
- return CURLE_OUT_OF_MEMORY;
- }
-
- qs->conn_ref.get_conn = get_conn;
- qs->conn_ref.user_data = qs;
-
- return CURLE_OK;
-}
-
-/*
- * Store ngtcp2 version info in this buffer.
- */
-void Curl_quic_ver(char *p, size_t len)
-{
- const ngtcp2_info *ng2 = ngtcp2_version(0);
- const nghttp3_info *ht3 = nghttp3_version(0);
- (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
- ng2->version_str, ht3->version_str);
-}
-
-static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t *socks)
-{
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- /* in an HTTP/2 connection we can basically always get a frame so we should
- always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
-
- /* we're still uploading or the HTTP/2 layer wants to send data */
- if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND &&
- (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
- ngtcp2_conn_get_cwnd_left(qs->qconn) &&
- ngtcp2_conn_get_max_data_left(qs->qconn) &&
- nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id))
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
-
- return bitmap;
-}
-
-static void qs_disconnect(struct quicsocket *qs)
-{
- char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
- ngtcp2_tstamp ts;
- ngtcp2_ssize rc;
-
- if(!qs->conn) /* already closed */
- return;
- ts = timestamp();
- rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */
- NULL, /* pkt_info */
- (uint8_t *)buffer, sizeof(buffer),
- &qs->last_error, ts);
- if(rc > 0) {
- while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) &&
- SOCKERRNO == EINTR);
- }
-
- qs->conn = NULL;
- if(qs->qlogfd != -1) {
- close(qs->qlogfd);
- qs->qlogfd = -1;
- }
-#ifdef USE_OPENSSL
- if(qs->ssl)
- SSL_free(qs->ssl);
- qs->ssl = NULL;
- SSL_CTX_free(qs->sslctx);
-#elif defined(USE_GNUTLS)
- if(qs->gtls) {
- if(qs->gtls->cred)
- gnutls_certificate_free_credentials(qs->gtls->cred);
- if(qs->gtls->session)
- gnutls_deinit(qs->gtls->session);
- free(qs->gtls);
- qs->gtls = NULL;
- }
-#elif defined(USE_WOLFSSL)
- if(qs->ssl)
- wolfSSL_free(qs->ssl);
- qs->ssl = NULL;
- wolfSSL_CTX_free(qs->sslctx);
-#endif
- free(qs->pktbuf);
- nghttp3_conn_del(qs->h3conn);
- ngtcp2_conn_del(qs->qconn);
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- int tempindex)
-{
- (void)data;
- if(conn->transport == TRNSPRT_QUIC)
- qs_disconnect(&conn->hequic[tempindex]);
-}
-
-static CURLcode ng_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- (void)dead_connection;
- Curl_quic_disconnect(data, conn, 0);
- Curl_quic_disconnect(data, conn, 1);
- return CURLE_OK;
-}
-
-static unsigned int ng_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- return CONNRESULT_NONE;
-}
-
-static const struct Curl_handler Curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- ng_getsock, /* proto_getsock */
- ng_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ng_getsock, /* perform_getsock */
- ng_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ng_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-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_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- (void)conn;
- (void)stream_id;
- (void)app_error_code;
- (void)user_data;
- H3BUGF(infof(data, "cb_h3_stream_close CALLED"));
-
- stream->closed = TRUE;
- stream->error3 = app_error_code;
- Curl_expire(data, 0, EXPIRE_QUIC);
- /* make sure that ngh3_stream_recv is called again to complete the transfer
- even if there are no more packets to be received from the server. */
- data->state.drain = 1;
- return 0;
-}
-
-/*
- * write_data() copies data to the stream's receive buffer. If not enough
- * space is available in the receive buffer, it copies the rest to the
- * stream's overflow buffer.
- */
-static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen)
-{
- CURLcode result = CURLE_OK;
- const char *buf = mem;
- size_t ncopy = memlen;
- /* copy as much as possible to the receive buffer */
- if(stream->len) {
- size_t len = CURLMIN(ncopy, stream->len);
- memcpy(stream->mem, buf, len);
- stream->len -= len;
- stream->memlen += len;
- stream->mem += len;
- buf += len;
- ncopy -= len;
- }
- /* copy the rest to the overflow buffer */
- if(ncopy)
- result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
- return result;
-}
-
-static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
- const uint8_t *buf, size_t buflen,
- void *user_data, void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
-
- result = write_data(stream, buf, buflen);
- if(result) {
- return -1;
- }
- stream->unacked_window += buflen;
- (void)stream_id;
- (void)user_data;
- return 0;
-}
-
-static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
- size_t consumed, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = user_data;
- (void)conn;
- (void)stream_user_data;
- (void)stream_id;
-
- ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
- ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
- return 0;
-}
-
-/* Decode HTTP status code. Returns -1 if no valid status code was
- decoded. (duplicate from http2.c) */
-static int decode_status_code(const uint8_t *value, size_t len)
-{
- int i;
- int res;
-
- if(len != 3) {
- return -1;
- }
-
- res = 0;
-
- for(i = 0; i < 3; ++i) {
- char c = value[i];
-
- if(c < '0' || c > '9') {
- return -1;
- }
-
- res *= 10;
- res += c - '0';
- }
-
- return res;
-}
-
-static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
- int fin, void *user_data, void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
- (void)stream_id;
- (void)user_data;
- (void)fin;
-
- /* add a CRLF only if we've received some headers */
- if(stream->firstheader) {
- result = write_data(stream, "\r\n", 2);
- if(result) {
- return -1;
- }
- }
-
- if(stream->status_code / 100 != 1) {
- stream->bodystarted = TRUE;
- }
- 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)
-{
- nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
- nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
- (void)stream_id;
- (void)token;
- (void)flags;
- (void)user_data;
-
- if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
- char line[14]; /* status line is always 13 characters long */
- size_t ncopy;
- stream->status_code = decode_status_code(h3val.base, h3val.len);
- DEBUGASSERT(stream->status_code != -1);
- ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
- stream->status_code);
- result = write_data(stream, line, ncopy);
- if(result) {
- return -1;
- }
- }
- else {
- /* store as an HTTP1-style header */
- result = write_data(stream, h3name.base, h3name.len);
- if(result) {
- return -1;
- }
- result = write_data(stream, ": ", 2);
- if(result) {
- return -1;
- }
- result = write_data(stream, h3val.base, h3val.len);
- if(result) {
- return -1;
- }
- result = write_data(stream, "\r\n", 2);
- if(result) {
- return -1;
- }
- }
-
- stream->firstheader = TRUE;
- 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 quicsocket *qs = user_data;
- int rv;
- (void)conn;
- (void)stream_user_data;
-
- rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code);
- if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_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 quicsocket *qs = user_data;
- int rv;
- (void)conn;
- (void)stream_user_data;
-
- rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code);
- if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static nghttp3_callbacks ngh3_callbacks = {
- cb_h3_acked_stream_data, /* 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 */
-};
-
-static int init_ngh3_conn(struct quicsocket *qs)
-{
- CURLcode result;
- int rc;
- int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
-
- if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- nghttp3_settings_default(&qs->h3settings);
-
- rc = nghttp3_conn_client_new(&qs->h3conn,
- &ngh3_callbacks,
- &qs->h3settings,
- nghttp3_mem_default(),
- qs);
- if(rc) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
- qpack_dec_stream_id);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- return CURLE_OK;
- fail:
-
- return result;
-}
-
-static Curl_recv ngh3_stream_recv;
-static Curl_send ngh3_stream_send;
-
-static size_t drain_overflow_buffer(struct HTTP *stream)
-{
- size_t overlen = Curl_dyn_len(&stream->overflow);
- size_t ncopy = CURLMIN(overlen, stream->len);
- if(ncopy > 0) {
- memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy);
- stream->len -= ncopy;
- stream->mem += ncopy;
- stream->memlen += ncopy;
- if(ncopy != overlen)
- /* make the buffer only keep the tail */
- (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
- else
- Curl_dyn_reset(&stream->overflow);
- }
- return ncopy;
-}
-
-/* incoming data frames on the h3 stream */
-static ssize_t ngh3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
-
- if(!stream->memlen) {
- /* remember where to store incoming data for this stream and how big the
- buffer is */
- stream->mem = buf;
- stream->len = buffersize;
- }
- /* else, there's data in the buffer already */
-
- /* if there's data in the overflow buffer from a previous call, copy as much
- as possible to the receive buffer before receiving more */
- drain_overflow_buffer(stream);
-
- if(ng_process_ingress(data, sockfd, qs)) {
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- if(stream->memlen) {
- ssize_t memlen = stream->memlen;
- /* data arrived */
- *curlcode = CURLE_OK;
- /* reset to allow more data to come */
- stream->memlen = 0;
- stream->mem = buf;
- stream->len = buffersize;
- /* extend the stream window with the data we're consuming and send out
- any additional packets to tell the server that we can receive more */
- extend_stream_window(qs->qconn, stream);
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- return memlen;
- }
-
- if(stream->closed) {
- if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
- failf(data,
- "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64
- ")",
- stream->stream3_id, stream->error3);
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- if(!stream->bodystarted) {
- failf(data,
- "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
- " all response header fields, treated as error",
- stream->stream3_id);
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- *curlcode = CURLE_OK;
- return 0;
- }
-
- infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN");
- *curlcode = CURLE_AGAIN;
- return -1;
-}
-
-/* this amount of data has now been acked on this stream */
-static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
- uint64_t datalen, void *user_data,
- void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- (void)user_data;
-
- if(!data->set.postfields) {
- stream->h3out->used -= datalen;
- H3BUGF(infof(data,
- "cb_h3_acked_stream_data, %zd bytes, %zd left unacked",
- datalen, stream->h3out->used));
- DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
-
- if(stream->h3out->used == 0) {
- int rv = nghttp3_conn_resume_stream(conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
- }
- }
- return 0;
-}
-
-static nghttp3_ssize cb_h3_readfunction(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_easy *data = stream_user_data;
- size_t nread;
- struct HTTP *stream = data->req.p.http;
- (void)conn;
- (void)stream_id;
- (void)user_data;
- (void)veccnt;
-
- if(data->set.postfields) {
- vec[0].base = data->set.postfields;
- vec[0].len = data->state.infilesize;
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- return 1;
- }
-
- if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
- return NGHTTP3_ERR_WOULDBLOCK;
- }
-
- nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
- if(nread > 0) {
- /* nghttp3 wants us to hold on to the data until it tells us it is okay to
- delete it. Append the data at the end of the h3out buffer. Since we can
- only return consecutive data, copy the amount that fits and the next
- part comes in next invoke. */
- struct h3out *out = stream->h3out;
- if(nread + out->windex > H3_SEND_SIZE)
- nread = H3_SEND_SIZE - out->windex;
-
- memcpy(&out->buf[out->windex], stream->upload_mem, nread);
-
- /* that's the chunk we return to nghttp3 */
- vec[0].base = &out->buf[out->windex];
- vec[0].len = nread;
-
- out->windex += nread;
- out->used += nread;
-
- if(out->windex == H3_SEND_SIZE)
- out->windex = 0; /* wrap */
- stream->upload_mem += nread;
- stream->upload_len -= nread;
- if(data->state.infilesize != -1) {
- stream->upload_left -= nread;
- if(!stream->upload_left)
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- }
- H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
- nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
- out->used));
- }
- if(stream->upload_done && !stream->upload_len &&
- (stream->upload_left <= 0)) {
- H3BUGF(infof(data, "cb_h3_readfunction sets EOF"));
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- return nread ? 1 : 0;
- }
- else if(!nread) {
- return NGHTTP3_ERR_WOULDBLOCK;
- }
- return 1;
-}
-
-/* Index where :authority header field will appear in request header
- field list. */
-#define AUTHORITY_DST_IDX 3
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- size_t nheader;
- struct quicsocket *qs = conn->quic;
- CURLcode result = CURLE_OK;
- nghttp3_nv *nva = NULL;
- int64_t stream3_id;
- int rc;
- struct h3out *h3out = NULL;
- struct h2h3req *hreq = NULL;
-
- rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
- if(rc) {
- failf(data, "can get bidi streams");
- result = CURLE_SEND_ERROR;
- goto fail;
- }
-
- stream->stream3_id = stream3_id;
- stream->h3req = TRUE; /* senf off! */
- Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
-
- result = Curl_pseudo_headers(data, mem, len, &hreq);
- if(result)
- goto fail;
- nheader = hreq->entries;
-
- nva = malloc(sizeof(nghttp3_nv) * nheader);
- if(!nva) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- else {
- unsigned int i;
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].namelen = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].valuelen = hreq->header[i].valuelen;
- nva[i].flags = NGHTTP3_NV_FLAG_NONE;
- }
- }
-
- switch(data->state.httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- case HTTPREQ_PUT: {
- nghttp3_data_reader data_reader;
- 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, but not zero */
-
- data_reader.read_data = cb_h3_readfunction;
-
- h3out = calloc(sizeof(struct h3out), 1);
- if(!h3out) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- stream->h3out = h3out;
-
- rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
- nva, nheader, &data_reader, data);
- if(rc) {
- result = CURLE_SEND_ERROR;
- goto fail;
- }
- break;
- }
- default:
- stream->upload_left = 0; /* nothing left to send */
- rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
- nva, nheader, NULL, data);
- if(rc) {
- result = CURLE_SEND_ERROR;
- goto fail;
- }
- break;
- }
-
- Curl_safefree(nva);
-
- infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
- stream3_id, (void *)data);
-
- Curl_pseudo_free(hreq);
- return CURLE_OK;
-
-fail:
- free(nva);
- Curl_pseudo_free(hreq);
- return result;
-}
-static ssize_t ngh3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- ssize_t sent = 0;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
-
- if(stream->closed) {
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- if(!stream->h3req) {
- CURLcode result = http_request(data, mem, len);
- if(result) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- /* Assume that mem of length len only includes HTTP/1.1 style
- header fields. In other words, it does not contain request
- body. */
- sent = len;
- }
- else {
- H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes",
- len));
- if(!stream->upload_len) {
- stream->upload_mem = mem;
- stream->upload_len = len;
- (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
- }
- else {
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- }
-
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- /* Reset post upload buffer after resumed. */
- if(stream->upload_mem) {
- if(data->set.postfields) {
- sent = len;
- }
- else {
- sent = len - stream->upload_len;
- }
-
- stream->upload_mem = NULL;
- stream->upload_len = 0;
-
- if(sent == 0) {
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- }
-
- *curlcode = CURLE_OK;
- return sent;
-}
-
-static CURLcode ng_has_connected(struct Curl_easy *data,
- struct connectdata *conn, int tempindex)
-{
- CURLcode result = CURLE_OK;
- const char *hostname, *disp_hostname;
- int port;
- char *snihost;
-
- Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
- snihost = Curl_ssl_snihost(data, hostname, NULL);
- if(!snihost)
- return CURLE_PEER_FAILED_VERIFICATION;
-
- conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
- conn->send[FIRSTSOCKET] = ngh3_stream_send;
- conn->handler = &Curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
- conn->quic = &conn->hequic[tempindex];
-
- if(conn->ssl_config.verifyhost) {
-#ifdef USE_OPENSSL
- X509 *server_cert;
- server_cert = SSL_get_peer_certificate(conn->quic->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, conn, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
-#elif defined(USE_GNUTLS)
- result = Curl_gtls_verifyserver(data, conn->quic->gtls->session,
- &conn->ssl_config, &data->set.ssl,
- hostname, disp_hostname,
- data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)
- return CURLE_PEER_FAILED_VERIFICATION;
-#endif
- infof(data, "Verified certificate just fine");
- }
- else
- infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, conn->quic->ssl);
-#endif
- return result;
-}
-
-/*
- * There can be multiple connection attempts going on in parallel.
- */
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *done)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- curl_socket_t sockfd = conn->tempsock[sockindex];
-
- result = ng_process_ingress(data, sockfd, qs);
- if(result)
- goto error;
-
- result = ng_flush_egress(data, sockfd, qs);
- if(result)
- goto error;
-
- if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
- result = ng_has_connected(data, conn, sockindex);
- if(!result)
- *done = TRUE;
- }
-
- return result;
- error:
- (void)qs_disconnect(qs);
- return result;
-
-}
-
-static CURLcode ng_process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs)
-{
- ssize_t recvd;
- int rv;
- uint8_t buf[65536];
- size_t bufsize = sizeof(buf);
- struct sockaddr_storage remote_addr;
- socklen_t remote_addrlen;
- ngtcp2_path path;
- ngtcp2_tstamp ts = timestamp();
- ngtcp2_pkt_info pi = { 0 };
-
- for(;;) {
- remote_addrlen = sizeof(remote_addr);
- while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0,
- (struct sockaddr *)&remote_addr,
- &remote_addrlen)) == -1 &&
- SOCKERRNO == EINTR)
- ;
- if(recvd == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)
- break;
-
- failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd);
- return CURLE_RECV_ERROR;
- }
-
- ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen);
- ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr,
- remote_addrlen);
-
- rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts);
- if(rv) {
- if(!qs->last_error.error_code) {
- if(rv == NGTCP2_ERR_CRYPTO) {
- ngtcp2_connection_close_error_set_transport_error_tls_alert(
- &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0);
- }
- else {
- ngtcp2_connection_close_error_set_transport_error_liberr(
- &qs->last_error, rv, NULL, 0);
- }
- }
-
- if(rv == NGTCP2_ERR_CRYPTO)
- /* this is a "TLS problem", but a failed certificate verification
- is a common reason for this */
- return CURLE_PEER_FAILED_VERIFICATION;
- return CURLE_RECV_ERROR;
- }
- }
-
- return CURLE_OK;
-}
-
-static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen);
-
-static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data,
- int sockfd, struct quicsocket *qs,
- const uint8_t *pkt, size_t pktlen,
- size_t gsolen)
-{
- const uint8_t *p, *end = pkt + pktlen;
- size_t sent;
-
- *psent = 0;
-
- for(p = pkt; p < end; p += gsolen) {
- size_t len = CURLMIN(gsolen, (size_t)(end - p));
- CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len);
- if(curlcode != CURLE_OK) {
- return curlcode;
- }
- *psent += sent;
- }
-
- return CURLE_OK;
-}
-
-static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
-#ifdef HAVE_SENDMSG
- struct iovec msg_iov;
- struct msghdr msg = {0};
- ssize_t sent;
-#if defined(__linux__) && defined(UDP_SEGMENT)
- uint8_t msg_ctrl[32];
- struct cmsghdr *cm;
-#endif
-
- *psent = 0;
- msg_iov.iov_base = (uint8_t *)pkt;
- msg_iov.iov_len = pktlen;
- msg.msg_iov = &msg_iov;
- msg.msg_iovlen = 1;
-
-#if defined(__linux__) && defined(UDP_SEGMENT)
- if(pktlen > gsolen) {
- /* Only set this, when we need it. macOS, for example,
- * does not seem to like a msg_control of length 0. */
- msg.msg_control = msg_ctrl;
- assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
- msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
- cm = CMSG_FIRSTHDR(&msg);
- cm->cmsg_level = SOL_UDP;
- cm->cmsg_type = UDP_SEGMENT;
- cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
- *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
- }
-#endif
-
-
- while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
- ;
-
- if(sent == -1) {
- switch(SOCKERRNO) {
- case EAGAIN:
-#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
-#endif
- return CURLE_AGAIN;
- case EMSGSIZE:
- /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
- break;
- case EIO:
- if(pktlen > gsolen) {
- /* GSO failure */
- failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
- SOCKERRNO);
- qs->no_gso = TRUE;
- return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen,
- gsolen);
- }
- /* FALLTHROUGH */
- default:
- failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
- return CURLE_SEND_ERROR;
- }
- }
- else {
- assert(pktlen == (size_t)sent);
- }
-#else
- ssize_t sent;
- (void)qs;
- (void)gsolen;
-
- *psent = 0;
-
- while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 &&
- SOCKERRNO == EINTR)
- ;
-
- if(sent == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
- return CURLE_AGAIN;
- }
- else {
- failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
- if(SOCKERRNO != EMSGSIZE) {
- return CURLE_SEND_ERROR;
- }
- /* UDP datagram is too large; caused by PMTUD. Just let it be
- lost. */
- }
- }
-#endif
-
- *psent = pktlen;
-
- return CURLE_OK;
-}
-
-static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
- if(qs->no_gso && pktlen > gsolen) {
- return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen);
- }
-
- return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen);
-}
-
-static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
- struct blocked_pkt *blkpkt;
-
- assert(qs->num_blocked_pkt <
- sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0]));
-
- blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++];
-
- blkpkt->pkt = pkt;
- blkpkt->pktlen = pktlen;
- blkpkt->gsolen = gsolen;
-}
-
-static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- size_t sent;
- CURLcode curlcode;
- struct blocked_pkt *blkpkt;
-
- for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt;
- ++qs->num_blocked_pkt_sent) {
- blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent];
- curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt,
- blkpkt->pktlen, blkpkt->gsolen);
-
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- blkpkt->pkt += sent;
- blkpkt->pktlen -= sent;
- }
- return curlcode;
- }
- }
-
- qs->num_blocked_pkt = 0;
- qs->num_blocked_pkt_sent = 0;
-
- return CURLE_OK;
-}
-
-static CURLcode ng_flush_egress(struct Curl_easy *data,
- int sockfd,
- struct quicsocket *qs)
-{
- int rv;
- size_t sent;
- ngtcp2_ssize outlen;
- uint8_t *outpos = qs->pktbuf;
- size_t max_udp_payload_size =
- ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn);
- size_t path_max_udp_payload_size =
- ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn);
- size_t max_pktcnt =
- CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size);
- size_t pktcnt = 0;
- size_t gsolen;
- ngtcp2_path_storage ps;
- ngtcp2_tstamp ts = timestamp();
- ngtcp2_tstamp expiry;
- ngtcp2_duration timeout;
- int64_t stream_id;
- nghttp3_ssize veccnt;
- int fin;
- nghttp3_vec vec[16];
- ngtcp2_ssize ndatalen;
- uint32_t flags;
- CURLcode curlcode;
-
- rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
- if(rv) {
- failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
- ngtcp2_strerror(rv));
- ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error,
- rv, NULL, 0);
- return CURLE_SEND_ERROR;
- }
-
- if(qs->num_blocked_pkt) {
- curlcode = send_blocked_pkt(data, sockfd, qs);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- }
-
- ngtcp2_path_storage_zero(&ps);
-
- for(;;) {
- veccnt = 0;
- stream_id = -1;
- fin = 0;
-
- if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
- veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
- sizeof(vec) / sizeof(vec[0]));
- if(veccnt < 0) {
- failf(data, "nghttp3_conn_writev_stream returned error: %s",
- nghttp3_strerror((int)veccnt));
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error,
- nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
- return CURLE_SEND_ERROR;
- }
- }
-
- flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
- (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
- outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos,
- max_udp_payload_size,
- &ndatalen, flags, stream_id,
- (const ngtcp2_vec *)vec, veccnt, ts);
- if(outlen == 0) {
- if(outpos != qs->pktbuf) {
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
- gsolen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- }
-
- break;
- }
- if(outlen < 0) {
- switch(outlen) {
- case NGTCP2_ERR_STREAM_DATA_BLOCKED:
- assert(ndatalen == -1);
- nghttp3_conn_block_stream(qs->h3conn, stream_id);
- continue;
- case NGTCP2_ERR_STREAM_SHUT_WR:
- assert(ndatalen == -1);
- nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
- continue;
- case NGTCP2_ERR_WRITE_MORE:
- assert(ndatalen >= 0);
- rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
- if(rv) {
- failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
- nghttp3_strerror(rv));
- return CURLE_SEND_ERROR;
- }
- continue;
- default:
- assert(ndatalen == -1);
- failf(data, "ngtcp2_conn_writev_stream returned error: %s",
- ngtcp2_strerror((int)outlen));
- ngtcp2_connection_close_error_set_transport_error_liberr(
- &qs->last_error, (int)outlen, NULL, 0);
- return CURLE_SEND_ERROR;
- }
- }
- else if(ndatalen >= 0) {
- rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
- if(rv) {
- failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
- nghttp3_strerror(rv));
- return CURLE_SEND_ERROR;
- }
- }
-
- outpos += outlen;
-
- if(pktcnt == 0) {
- gsolen = outlen;
- }
- else if((size_t)outlen > gsolen ||
- (gsolen > path_max_udp_payload_size &&
- (size_t)outlen != gsolen)) {
- /* Packet larger than path_max_udp_payload_size is PMTUD probe
- packet and it might not be sent because of EMSGSIZE. Send
- them separately to minimize the loss. */
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - outlen - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent,
- outpos - outlen - qs->pktbuf - sent, gsolen);
- push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen,
- outlen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- assert(0 == sent);
- push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
-
- pktcnt = 0;
- outpos = qs->pktbuf;
- continue;
- }
-
- if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
- gsolen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
-
- pktcnt = 0;
- outpos = qs->pktbuf;
- }
- }
-
- expiry = ngtcp2_conn_get_expiry(qs->qconn);
- if(expiry != UINT64_MAX) {
- if(expiry <= ts) {
- timeout = 0;
- }
- else {
- timeout = expiry - ts;
- if(timeout % NGTCP2_MILLISECONDS) {
- timeout += NGTCP2_MILLISECONDS;
- }
- }
- Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
- */
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- DEBUGASSERT(conn);
- if(conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- stream->upload_done = TRUE;
- (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from http.c:Curl_http_done when a request completes.
- */
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- (void)premature;
- if(data->conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- struct HTTP *stream = data->req.p.http;
- Curl_dyn_free(&stream->overflow);
- free(stream->h3out);
- }
-}
-
-/*
- * Called from transfer.c:data_pending to know if we should keep looping
- * to receive more data from the connection.
- */
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- /* We may have received more data than we're able to hold in the receive
- buffer and allocated an overflow buffer. Since it's possible that
- there's no more data coming on the socket, we need to keep reading
- until the overflow buffer is empty. */
- const struct HTTP *stream = data->req.p.http;
- return Curl_dyn_len(&stream->overflow) > 0;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
- struct quicsocket *qs = conn->quic;
-
- if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) {
- return CURLE_OK;
- }
-
- if(ng_flush_egress(data, sockfd, qs)) {
- return CURLE_SEND_ERROR;
- }
-
- return CURLE_OK;
-}
-
-#endif
diff --git a/Utilities/cmcurl/lib/vquic/quiche.c b/Utilities/cmcurl/lib/vquic/quiche.c
deleted file mode 100644
index 2b9a0410f1..0000000000
--- a/Utilities/cmcurl/lib/vquic/quiche.c
+++ /dev/null
@@ -1,892 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_QUICHE
-#include <quiche.h>
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "quic.h"
-#include "strcase.h"
-#include "multiif.h"
-#include "connect.h"
-#include "strerror.h"
-#include "vquic.h"
-#include "transfer.h"
-#include "h2h3.h"
-#include "vtls/openssl.h"
-#include "vtls/keylog.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define DEBUG_HTTP3
-/* #define DEBUG_QUICHE */
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define QUIC_MAX_STREAMS (256*1024)
-#define QUIC_MAX_DATA (1*1024*1024)
-#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
-
-static CURLcode process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs);
-
-static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
- struct quicsocket *qs);
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len);
-static Curl_recv h3_stream_recv;
-static Curl_send h3_stream_send;
-
-static int quiche_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks)
-{
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- /* in an HTTP/2 connection we can basically always get a frame so we should
- always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
-
- /* we're still uploading or the HTTP/2 layer wants to send data */
- if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
-
- return bitmap;
-}
-
-static CURLcode qs_disconnect(struct Curl_easy *data,
- struct quicsocket *qs)
-{
- DEBUGASSERT(qs);
- if(qs->conn) {
- (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0);
- /* flushing the egress is not a failsafe way to deliver all the
- outstanding packets, but we also don't want to get stuck here... */
- (void)flush_egress(data, qs->sockfd, qs);
- quiche_conn_free(qs->conn);
- qs->conn = NULL;
- }
- if(qs->h3config)
- quiche_h3_config_free(qs->h3config);
- if(qs->h3c)
- quiche_h3_conn_free(qs->h3c);
- if(qs->cfg) {
- quiche_config_free(qs->cfg);
- qs->cfg = NULL;
- }
- return CURLE_OK;
-}
-
-static CURLcode quiche_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- struct quicsocket *qs = conn->quic;
- (void)dead_connection;
- return qs_disconnect(data, qs);
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- int tempindex)
-{
- if(conn->transport == TRNSPRT_QUIC)
- qs_disconnect(data, &conn->hequic[tempindex]);
-}
-
-static unsigned int quiche_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- return CONNRESULT_NONE;
-}
-
-static CURLcode quiche_do(struct Curl_easy *data, bool *done)
-{
- struct HTTP *stream = data->req.p.http;
- stream->h3req = FALSE; /* not sent */
- return Curl_http(data, done);
-}
-
-static const struct Curl_handler Curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- quiche_do, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- quiche_getsock, /* proto_getsock */
- quiche_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- quiche_getsock, /* perform_getsock */
- quiche_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- quiche_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-#ifdef DEBUG_QUICHE
-static void quiche_debug_log(const char *line, void *argp)
-{
- (void)argp;
- fprintf(stderr, "%s\n", line);
-}
-#endif
-
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
- SSL_CTX_set_alpn_protos(ssl_ctx,
- (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
- }
-
- {
- struct connectdata *conn = data->conn;
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
- if(ssl_cafile || ssl_capath) {
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell OpenSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- }
- return ssl_ctx;
-}
-
-static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn)
-{
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = conn->host.name;
-
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- SSL_set_app_data(qs->ssl, qs);
-
- /* set SNI */
- SSL_set_tlsext_host_name(qs->ssl, hostname);
- return 0;
-}
-
-
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr, socklen_t addrlen)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- char ipbuf[40];
- int port;
- int rv;
-
-#ifdef DEBUG_QUICHE
- /* initialize debug log callback only once */
- static int debug_log_init = 0;
- if(!debug_log_init) {
- quiche_enable_debug_logging(quiche_debug_log, NULL);
- debug_log_init = 1;
- }
-#endif
-
- (void)addr;
- (void)addrlen;
-
- qs->sockfd = sockfd;
- qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
- if(!qs->cfg) {
- failf(data, "can't create quiche config");
- return CURLE_FAILED_INIT;
- }
-
- quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
- quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
- QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
- quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
- quiche_config_set_application_protos(qs->cfg,
- (uint8_t *)
- QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- - 1);
-
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-
- if(quic_init_ssl(qs, conn))
- return CURLE_QUIC_CONNECT_ERROR;
-
- result = Curl_rand(data, qs->scid, sizeof(qs->scid));
- if(result)
- return result;
-
- qs->local_addrlen = sizeof(qs->local_addr);
- rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
- &qs->local_addrlen);
- if(rv == -1)
- return CURLE_QUIC_CONNECT_ERROR;
-
- qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid,
- sizeof(qs->scid), NULL, 0,
- (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen, addr, addrlen,
- qs->cfg, qs->ssl, false);
- if(!qs->conn) {
- failf(data, "can't create quiche connection");
- return CURLE_OUT_OF_MEMORY;
- }
-
- /* Known to not work on Windows */
-#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
- {
- int qfd;
- (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd);
- if(qfd != -1)
- quiche_conn_set_qlog_fd(qs->conn, qfd,
- "qlog title", "curl qlog");
- }
-#endif
-
- result = flush_egress(data, sockfd, qs);
- if(result)
- return result;
-
- /* extract the used address as a string */
- if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
- char buffer[STRERROR_LEN];
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- infof(data, "Connect socket %d over QUIC to %s:%ld",
- sockfd, ipbuf, port);
-
- Curl_persistconninfo(data, conn, NULL, -1);
-
- {
- unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
- unsigned alpn_len, offset = 0;
-
- /* Replace each ALPN length prefix by a comma. */
- while(offset < sizeof(alpn_protocols) - 1) {
- alpn_len = alpn_protocols[offset];
- alpn_protocols[offset] = ',';
- offset += 1 + alpn_len;
- }
-
- infof(data, "Sent QUIC client Initial, ALPN: %s",
- alpn_protocols + 1);
- }
-
- return CURLE_OK;
-}
-
-static CURLcode quiche_has_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- int tempindex)
-{
- CURLcode result;
- struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
-
- conn->recv[sockindex] = h3_stream_recv;
- conn->send[sockindex] = h3_stream_send;
- conn->handler = &Curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-
- if(conn->ssl_config.verifyhost) {
- X509 *server_cert;
- server_cert = SSL_get_peer_certificate(qs->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, conn, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
- infof(data, "Verified certificate just fine");
- }
- else
- infof(data, "Skipped certificate verification");
-
- qs->h3config = quiche_h3_config_new();
- if(!qs->h3config)
- return CURLE_OUT_OF_MEMORY;
-
- /* Create a new HTTP/3 connection on the QUIC connection. */
- qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
- if(!qs->h3c) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- if(conn->hequic[1-tempindex].cfg) {
- qs = &conn->hequic[1-tempindex];
- quiche_config_free(qs->cfg);
- quiche_conn_free(qs->conn);
- qs->cfg = NULL;
- qs->conn = NULL;
- }
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, qs->ssl);
-
- return CURLE_OK;
- fail:
- quiche_h3_config_free(qs->h3config);
- quiche_h3_conn_free(qs->h3c);
- return result;
-}
-
-/*
- * This function gets polled to check if this QUIC connection has connected.
- */
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *done)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- curl_socket_t sockfd = conn->tempsock[sockindex];
-
- result = process_ingress(data, sockfd, qs);
- if(result)
- goto error;
-
- result = flush_egress(data, sockfd, qs);
- if(result)
- goto error;
-
- if(quiche_conn_is_established(qs->conn)) {
- *done = TRUE;
- result = quiche_has_connected(data, conn, 0, sockindex);
- DEBUGF(infof(data, "quiche established connection"));
- }
-
- return result;
- error:
- qs_disconnect(data, qs);
- return result;
-}
-
-static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- ssize_t recvd;
- uint8_t *buf = (uint8_t *)data->state.buffer;
- size_t bufsize = data->set.buffer_size;
- struct sockaddr_storage from;
- socklen_t from_len;
- quiche_recv_info recv_info;
-
- DEBUGASSERT(qs->conn);
-
- /* in case the timeout expired */
- quiche_conn_on_timeout(qs->conn);
-
- do {
- from_len = sizeof(from);
-
- recvd = recvfrom(sockfd, buf, bufsize, 0,
- (struct sockaddr *)&from, &from_len);
-
- if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)))
- break;
-
- if(recvd < 0) {
- failf(data, "quiche: recvfrom() unexpectedly returned %zd "
- "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
- return CURLE_RECV_ERROR;
- }
-
- recv_info.from = (struct sockaddr *) &from;
- recv_info.from_len = from_len;
- recv_info.to = (struct sockaddr *) &qs->local_addr;
- recv_info.to_len = qs->local_addrlen;
-
- recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info);
- if(recvd == QUICHE_ERR_DONE)
- break;
-
- if(recvd < 0) {
- if(QUICHE_ERR_TLS_FAIL == recvd) {
- long verify_ok = SSL_get_verify_result(qs->ssl);
- if(verify_ok != X509_V_OK) {
- failf(data, "SSL certificate problem: %s",
- X509_verify_cert_error_string(verify_ok));
-
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- }
-
- failf(data, "quiche_conn_recv() == %zd", recvd);
-
- return CURLE_RECV_ERROR;
- }
- } while(1);
-
- return CURLE_OK;
-}
-
-/*
- * flush_egress drains the buffers and sends off data.
- * Calls failf() on errors.
- */
-static CURLcode flush_egress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- ssize_t sent;
- uint8_t out[1200];
- int64_t timeout_ns;
- quiche_send_info send_info;
-
- do {
- sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info);
- if(sent == QUICHE_ERR_DONE)
- break;
-
- if(sent < 0) {
- failf(data, "quiche_conn_send returned %zd", sent);
- return CURLE_SEND_ERROR;
- }
-
- sent = send(sockfd, out, sent, 0);
- if(sent < 0) {
- failf(data, "send() returned %zd", sent);
- return CURLE_SEND_ERROR;
- }
- } while(1);
-
- /* time until the next timeout event, as nanoseconds. */
- timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
- if(timeout_ns)
- /* expire uses milliseconds */
- Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
-
- return CURLE_OK;
-}
-
-struct h3h1header {
- char *dest;
- size_t destlen; /* left to use */
- size_t nlen; /* used */
-};
-
-static int cb_each_header(uint8_t *name, size_t name_len,
- uint8_t *value, size_t value_len,
- void *argp)
-{
- struct h3h1header *headers = (struct h3h1header *)argp;
- size_t olen = 0;
-
- if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
- msnprintf(headers->dest,
- headers->destlen, "HTTP/3 %.*s\n",
- (int) value_len, value);
- }
- else if(!headers->nlen) {
- return CURLE_HTTP3;
- }
- else {
- msnprintf(headers->dest,
- headers->destlen, "%.*s: %.*s\n",
- (int)name_len, name, (int) value_len, value);
- }
- olen = strlen(headers->dest);
- headers->destlen -= olen;
- headers->nlen += olen;
- headers->dest += olen;
- return 0;
-}
-
-static ssize_t h3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- ssize_t recvd = -1;
- ssize_t rcode;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- quiche_h3_event *ev;
- int rc;
- struct h3h1header headers;
- struct HTTP *stream = data->req.p.http;
- headers.dest = buf;
- headers.destlen = buffersize;
- headers.nlen = 0;
-
- if(process_ingress(data, sockfd, qs)) {
- infof(data, "h3_stream_recv returns on ingress");
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
-
- if(qs->h3_recving) {
- /* body receiving state */
- rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id,
- (unsigned char *)buf, buffersize);
- if(rcode <= 0) {
- recvd = -1;
- qs->h3_recving = FALSE;
- /* fall through into the while loop below */
- }
- else
- recvd = rcode;
- }
-
- while(recvd < 0) {
- int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
- if(s < 0)
- /* nothing more to do */
- break;
-
- if(s != stream->stream3_id) {
- /* another transfer, ignore for now */
- infof(data, "Got h3 for stream %u, expects %u",
- s, stream->stream3_id);
- continue;
- }
-
- switch(quiche_h3_event_type(ev)) {
- case QUICHE_H3_EVENT_HEADERS:
- rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
- if(rc) {
- *curlcode = rc;
- failf(data, "Error in HTTP/3 response header");
- break;
- }
- recvd = headers.nlen;
- break;
- case QUICHE_H3_EVENT_DATA:
- if(!stream->firstbody) {
- /* add a header-body separator CRLF */
- buf[0] = '\r';
- buf[1] = '\n';
- buf += 2;
- buffersize -= 2;
- stream->firstbody = TRUE;
- recvd = 2; /* two bytes already */
- }
- else
- recvd = 0;
- rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
- buffersize);
- if(rcode <= 0) {
- recvd = -1;
- break;
- }
- qs->h3_recving = TRUE;
- recvd += rcode;
- break;
-
- case QUICHE_H3_EVENT_RESET:
- streamclose(conn, "Stream reset");
- *curlcode = CURLE_PARTIAL_FILE;
- return -1;
-
- case QUICHE_H3_EVENT_FINISHED:
- streamclose(conn, "End of stream");
- recvd = 0; /* end of stream */
- break;
- default:
- break;
- }
-
- quiche_h3_event_free(ev);
- }
- if(flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
- if(recvd >= 0)
- /* Get this called again to drain the event queue */
- Curl_expire(data, 0, EXPIRE_QUIC);
-
- data->state.drain = (recvd >= 0) ? 1 : 0;
- return recvd;
-}
-
-static ssize_t h3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- ssize_t sent;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
-
- if(!stream->h3req) {
- CURLcode result = http_request(data, mem, len);
- if(result) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- sent = len;
- }
- else {
- sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
- (uint8_t *)mem, len, FALSE);
- if(sent == QUICHE_H3_ERR_DONE) {
- sent = 0;
- }
- else if(sent < 0) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- }
-
- if(flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- *curlcode = CURLE_OK;
- return sent;
-}
-
-/*
- * Store quiche version info in this buffer.
- */
-void Curl_quic_ver(char *p, size_t len)
-{
- (void)msnprintf(p, len, "quiche/%s", quiche_version());
-}
-
-/* Index where :authority header field will appear in request header
- field list. */
-#define AUTHORITY_DST_IDX 3
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- size_t nheader;
- int64_t stream3_id;
- quiche_h3_header *nva = NULL;
- struct quicsocket *qs = conn->quic;
- CURLcode result = CURLE_OK;
- struct h2h3req *hreq = NULL;
-
- stream->h3req = TRUE; /* senf off! */
-
- result = Curl_pseudo_headers(data, mem, len, &hreq);
- if(result)
- goto fail;
- nheader = hreq->entries;
-
- nva = malloc(sizeof(quiche_h3_header) * nheader);
- if(!nva) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- else {
- unsigned int i;
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].name_len = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].value_len = hreq->header[i].valuelen;
- }
- }
-
- switch(data->state.httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- case HTTPREQ_PUT:
- 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, but not zero */
-
- stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
- stream->upload_left ? FALSE: TRUE);
- if((stream3_id >= 0) && data->set.postfields) {
- ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
- (uint8_t *)data->set.postfields,
- stream->upload_left, TRUE);
- if(sent <= 0) {
- failf(data, "quiche_h3_send_body failed");
- result = CURLE_SEND_ERROR;
- }
- stream->upload_left = 0; /* nothing left to send */
- }
- break;
- default:
- stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
- TRUE);
- break;
- }
-
- Curl_safefree(nva);
-
- if(stream3_id < 0) {
- H3BUGF(infof(data, "quiche_h3_send_request returned %d",
- stream3_id));
- result = CURLE_SEND_ERROR;
- goto fail;
- }
-
- infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
- stream3_id, (void *)data);
- stream->stream3_id = stream3_id;
-
- Curl_pseudo_free(hreq);
- return CURLE_OK;
-
-fail:
- free(nva);
- Curl_pseudo_free(hreq);
- return result;
-}
-
-/*
- * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
- */
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- DEBUGASSERT(conn);
- if(conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- ssize_t sent;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- stream->upload_done = TRUE;
- sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
- NULL, 0, TRUE);
- if(sent < 0)
- return CURLE_SEND_ERROR;
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from http.c:Curl_http_done when a request completes.
- */
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- (void)data;
- (void)premature;
-}
-
-/*
- * Called from transfer.c:data_pending to know if we should keep looping
- * to receive more data from the connection.
- */
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- (void)data;
- return FALSE;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- (void)data;
- return CURLE_OK;
-}
-
-#endif
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index e52a4f301d..5f4f30d715 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,15 +24,26 @@
#include "curl_setup.h"
-#ifdef ENABLE_QUIC
-
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "urldata.h"
#include "dynbuf.h"
-#include "curl_printf.h"
+#include "cfilters.h"
+#include "curl_log.h"
+#include "curl_msh3.h"
+#include "curl_ngtcp2.h"
+#include "curl_quiche.h"
#include "vquic.h"
+#include "vquic_int.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#ifdef ENABLE_QUIC
#ifdef O_BINARY
#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
@@ -40,6 +51,231 @@
#define QLOGMODE O_WRONLY|O_CREAT
#endif
+void Curl_quic_ver(char *p, size_t len)
+{
+#ifdef USE_NGTCP2
+ Curl_ngtcp2_ver(p, len);
+#elif defined(USE_QUICHE)
+ Curl_quiche_ver(p, len);
+#elif defined(USE_MSH3)
+ Curl_msh3_ver(p, len);
+#endif
+}
+
+CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen)
+{
+ qctx->num_blocked_pkt = 0;
+ qctx->num_blocked_pkt_sent = 0;
+ memset(&qctx->blocked_pkt, 0, sizeof(qctx->blocked_pkt));
+
+ qctx->pktbuflen = pktbuflen;
+ qctx->pktbuf = malloc(qctx->pktbuflen);
+ if(!qctx->pktbuf)
+ return CURLE_OUT_OF_MEMORY;
+
+#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
+ qctx->no_gso = FALSE;
+#else
+ qctx->no_gso = TRUE;
+#endif
+
+ return CURLE_OK;
+}
+
+void vquic_ctx_free(struct cf_quic_ctx *qctx)
+{
+ free(qctx->pktbuf);
+ qctx->pktbuf = NULL;
+}
+
+static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen,
+ size_t gsolen, size_t *psent);
+
+static CURLcode do_sendmsg(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent)
+{
+#ifdef HAVE_SENDMSG
+ struct iovec msg_iov;
+ struct msghdr msg = {0};
+ ssize_t sent;
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ uint8_t msg_ctrl[32];
+ struct cmsghdr *cm;
+#endif
+
+ *psent = 0;
+ msg_iov.iov_base = (uint8_t *)pkt;
+ msg_iov.iov_len = pktlen;
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ if(pktlen > gsolen) {
+ /* Only set this, when we need it. macOS, for example,
+ * does not seem to like a msg_control of length 0. */
+ msg.msg_control = msg_ctrl;
+ assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
+ msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_UDP;
+ cm->cmsg_type = UDP_SEGMENT;
+ cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
+ *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
+ }
+#endif
+
+
+ while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ switch(SOCKERRNO) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ return CURLE_AGAIN;
+ case EMSGSIZE:
+ /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
+ break;
+ case EIO:
+ if(pktlen > gsolen) {
+ /* GSO failure */
+ failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
+ SOCKERRNO);
+ 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)", sent, SOCKERRNO);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else {
+ assert(pktlen == (size_t)sent);
+ }
+#else
+ ssize_t sent;
+ (void)gsolen;
+
+ *psent = 0;
+
+ while((sent = send(qctx->sockfd, (const char *)pkt, pktlen, 0)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ return CURLE_AGAIN;
+ }
+ else {
+ failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
+ if(SOCKERRNO != EMSGSIZE) {
+ return CURLE_SEND_ERROR;
+ }
+ /* UDP datagram is too large; caused by PMTUD. Just let it be
+ lost. */
+ }
+ }
+#endif
+ (void)cf;
+ *psent = pktlen;
+
+ return CURLE_OK;
+}
+
+static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen,
+ size_t gsolen, size_t *psent)
+{
+ const uint8_t *p, *end = pkt + pktlen;
+ size_t sent;
+
+ *psent = 0;
+
+ for(p = pkt; p < end; p += gsolen) {
+ size_t len = CURLMIN(gsolen, (size_t)(end - p));
+ CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
+ if(curlcode != CURLE_OK) {
+ return curlcode;
+ }
+ *psent += sent;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode vquic_send_packet(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent)
+{
+ if(qctx->no_gso && pktlen > gsolen) {
+ return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
+ }
+
+ return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
+}
+
+
+
+void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen)
+{
+ struct vquic_blocked_pkt *blkpkt;
+
+ (void)cf;
+ assert(qctx->num_blocked_pkt <
+ sizeof(qctx->blocked_pkt) / sizeof(qctx->blocked_pkt[0]));
+
+ blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt++];
+
+ blkpkt->pkt = pkt;
+ blkpkt->pktlen = pktlen;
+ blkpkt->gsolen = gsolen;
+}
+
+CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx)
+{
+ size_t sent;
+ CURLcode curlcode;
+ struct vquic_blocked_pkt *blkpkt;
+
+ (void)cf;
+ for(; qctx->num_blocked_pkt_sent < qctx->num_blocked_pkt;
+ ++qctx->num_blocked_pkt_sent) {
+ blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt_sent];
+ curlcode = vquic_send_packet(cf, data, qctx, blkpkt->pkt,
+ blkpkt->pktlen, blkpkt->gsolen, &sent);
+
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ blkpkt->pkt += sent;
+ blkpkt->pktlen -= sent;
+ }
+ return curlcode;
+ }
+ }
+
+ qctx->num_blocked_pkt = 0;
+ qctx->num_blocked_pkt_sent = 0;
+
+ return CURLE_OK;
+}
+
/*
* If the QLOGDIR environment variable is set, open and return a file
* descriptor to write the log to.
@@ -84,4 +320,76 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
return CURLE_OK;
}
+
+CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ (void)transport;
+ DEBUGASSERT(transport == TRNSPRT_QUIC);
+#ifdef USE_NGTCP2
+ return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
+#elif defined(USE_QUICHE)
+ return Curl_cf_quiche_create(pcf, data, conn, ai);
+#elif defined(USE_MSH3)
+ return Curl_cf_msh3_create(pcf, data, conn, ai);
+#else
+ *pcf = NULL;
+ (void)data;
+ (void)conn;
+ (void)ai;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+bool Curl_conn_is_http3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+#ifdef USE_NGTCP2
+ return Curl_conn_is_ngtcp2(data, conn, sockindex);
+#elif defined(USE_QUICHE)
+ return Curl_conn_is_quiche(data, conn, sockindex);
+#elif defined(USE_MSH3)
+ return Curl_conn_is_msh3(data, conn, sockindex);
+#else
+ return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (conn->httpversion == 30));
+#endif
+}
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+ if(!(conn->handler->flags & PROTOPT_SSL)) {
+ failf(data, "HTTP/3 requested for non-HTTPS URL");
+ return CURLE_URL_MALFORMAT;
+ }
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy) {
+ 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 a HTTP proxy");
+ return CURLE_URL_MALFORMAT;
+ }
#endif
+
+ return CURLE_OK;
+}
+
+#else /* ENABLE_QUIC */
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+ (void)conn;
+ (void)data;
+ DEBUGF(infof(data, "QUIC is not supported in this build"));
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* !ENABLE_QUIC */
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h
index 8f599a8f49..dc73957aaf 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.h
+++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,10 +27,38 @@
#include "curl_setup.h"
#ifdef ENABLE_QUIC
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_addrinfo;
+
+void Curl_quic_ver(char *p, size_t len);
+
CURLcode Curl_qlogdir(struct Curl_easy *data,
unsigned char *scid,
size_t scidlen,
int *qlogfdp);
-#endif
+
+
+CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+bool Curl_conn_is_http3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+
+extern struct Curl_cftype Curl_cft_http3;
+
+#else /* ENABLE_QUIC */
+
+#define Curl_conn_is_http3(a,b,c) FALSE
+
+#endif /* !ENABLE_QUIC */
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn);
#endif /* HEADER_CURL_VQUIC_QUIC_H */
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.h b/Utilities/cmcurl/lib/vquic/vquic_int.h
index 2265999e22..42aba39b06 100644
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.h
+++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_NGTCP2_H
-#define HEADER_CURL_VQUIC_NGTCP2_H
+#ifndef HEADER_CURL_VQUIC_QUIC_INT_H
+#define HEADER_CURL_VQUIC_QUIC_INT_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,68 +26,47 @@
#include "curl_setup.h"
-#ifdef USE_NGTCP2
+#ifdef ENABLE_QUIC
-#ifdef HAVE_NETINET_UDP_H
-#include <netinet/udp.h>
-#endif
-
-#include <ngtcp2/ngtcp2_crypto.h>
-#include <nghttp3/nghttp3.h>
-#ifdef USE_OPENSSL
-#include <openssl/ssl.h>
-#elif defined(USE_WOLFSSL)
-#include <wolfssl/options.h>
-#include <wolfssl/ssl.h>
-#include <wolfssl/quic.h>
-#endif
-
-struct gtls_instance;
-
-struct blocked_pkt {
+struct vquic_blocked_pkt {
const uint8_t *pkt;
size_t pktlen;
size_t gsolen;
};
-struct quicsocket {
- struct connectdata *conn; /* point back to the connection */
- ngtcp2_conn *qconn;
- ngtcp2_cid dcid;
- ngtcp2_cid scid;
- uint32_t version;
- ngtcp2_settings settings;
- ngtcp2_transport_params transport_params;
- ngtcp2_connection_close_error last_error;
- ngtcp2_crypto_conn_ref conn_ref;
-#ifdef USE_OPENSSL
- SSL_CTX *sslctx;
- SSL *ssl;
-#elif defined(USE_GNUTLS)
- struct gtls_instance *gtls;
-#elif defined(USE_WOLFSSL)
- WOLFSSL_CTX *sslctx;
- WOLFSSL *ssl;
-#endif
+struct cf_quic_ctx {
+ curl_socket_t sockfd;
struct sockaddr_storage local_addr;
socklen_t local_addrlen;
- bool no_gso;
+ struct vquic_blocked_pkt blocked_pkt[2];
uint8_t *pktbuf;
- size_t pktbuflen;
/* the number of entries in blocked_pkt */
size_t num_blocked_pkt;
- /* the number of processed entries in blocked_pkt */
size_t num_blocked_pkt_sent;
/* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */
- struct blocked_pkt blocked_pkt[2];
-
- nghttp3_conn *h3conn;
- nghttp3_settings h3settings;
- int qlogfd;
+ size_t pktbuflen;
+ /* the number of processed entries in blocked_pkt */
+ bool no_gso;
};
-#include "urldata.h"
+CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen);
+void vquic_ctx_free(struct cf_quic_ctx *qctx);
+
+CURLcode vquic_send_packet(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent);
+
+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_pkt(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx);
+
-#endif
+#endif /* !ENABLE_QUIC */
-#endif /* HEADER_CURL_VQUIC_NGTCP2_H */
+#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index d9fa58acf8..11153181d4 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017 - 2022 Red Hat, Inc.
+ * Copyright (C) Red Hat, Inc.
*
* Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
* Robert Kolcun, Andreas Schneider
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index ce9229f8fa..4703eb572d 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -144,7 +144,7 @@ const struct Curl_handler Curl_handler_scp = {
scp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
- ssh_attach,
+ ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -173,7 +173,7 @@ const struct Curl_handler Curl_handler_sftp = {
sftp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
- ssh_attach,
+ ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -840,6 +840,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
#endif
static const char * const hostkey_method_ssh_rsa
= "ssh-rsa";
+ static const char * const hostkey_method_ssh_rsa_all
+ = "rsa-sha2-256,rsa-sha2-512,ssh-rsa";
static const char * const hostkey_method_ssh_dss
= "ssh-dss";
@@ -914,7 +916,16 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
break;
#endif
case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
- hostkey_method = hostkey_method_ssh_rsa;
+#ifdef HAVE_LIBSSH2_VERSION
+ if(libssh2_version(0x010900))
+ /* since 1.9.0 libssh2_session_method_pref() works as expected */
+ hostkey_method = hostkey_method_ssh_rsa_all;
+ else
+#endif
+ /* old libssh2 which cannot correctly remove unsupported methods due
+ * to bug in src/kex.c or does not support the new methods anyways.
+ */
+ hostkey_method = hostkey_method_ssh_rsa;
break;
case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
hostkey_method = hostkey_method_ssh_dss;
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h
index 13bb8aa2d6..7c25555076 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c
index 6a8fb560d6..17d59ecd23 100644
--- a/Utilities/cmcurl/lib/vssh/wolfssh.c
+++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c
index d9c0ce0eed..7e3eb79ce8 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.c
+++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -58,7 +58,7 @@ struct ssl_backend_data {
unsigned char buf[BR_SSL_BUFSIZE_BIDI];
br_x509_trust_anchor *anchors;
size_t anchors_len;
- const char *protocols[2];
+ const char *protocols[ALPN_ENTRIES_MAX];
/* SSL client context is active */
bool active;
/* size of pending write, yet to be flushed */
@@ -691,29 +691,17 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
Curl_ssl_sessionid_unlock(data);
}
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
-
- /* NOTE: when adding more protocols here, increase the size of the
- * protocols array in `struct ssl_backend_data`.
- */
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ size_t i;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- backend->protocols[cur++] = ALPN_H2;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ backend->protocols[i] = connssl->alpn->entries[i];
}
-#endif
-
- backend->protocols[cur++] = ALPN_HTTP_1_1;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- br_ssl_engine_set_protocol_names(&backend->ctx.eng,
- backend->protocols, cur);
+ br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
@@ -862,26 +850,11 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(cf->conn->bits.tls_enable_alpn) {
- const char *protocol;
-
- protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
- if(protocol) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol);
+ const char *proto;
-#ifdef USE_HTTP2
- if(!strcmp(protocol, ALPN_H2))
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- else
-#endif
- if(!strcmp(protocol, ALPN_HTTP_1_1))
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- else
- infof(data, "ALPN, unrecognized protocol %s", protocol);
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
- }
- else
- infof(data, VTLS_INFOF_NO_ALPN);
+ proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto,
+ proto? strlen(proto) : 0);
}
if(ssl_config->primary.sessionid) {
@@ -977,7 +950,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
{
CURLcode ret;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.h b/Utilities/cmcurl/lib/vtls/bearssl.h
index 5125359961..b3651b092c 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.h
+++ b/Utilities/cmcurl/lib/vtls/bearssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c
index 2074dcaa48..59fd27ce4e 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.c
+++ b/Utilities/cmcurl/lib/vtls/gskit.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -499,7 +499,7 @@ static void cancel_async_handshake(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(BACKEND);
- if(QsoCancelOperation(cf->conn->sock[cf->sockindex], 0) > 0)
+ if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0)
QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
}
@@ -532,7 +532,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, int directions)
DEBUGASSERT(connssl_next->backend);
n = 1;
fds[0].fd = BACKEND->remotefd;
- fds[1].fd = cf->conn->sock[cf->sockindex];
+ fds[1].fd = Curl_conn_cf_get_socket(cf, data);
if(directions & SOS_READ) {
fds[0].events |= POLLOUT;
@@ -847,7 +847,7 @@ static CURLcode gskit_connect_step1(struct Curl_cfilter *cf,
result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1);
if(!result)
result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0?
- BACKEND->localfd: cf->conn->sock[cf->sockindex]);
+ BACKEND->localfd: Curl_conn_cf_get_socket(cf, data));
if(!result)
result = set_ciphers(cf, data, BACKEND->handle, &protoflags);
if(!protoflags) {
@@ -1208,7 +1208,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
close_one(cf, data);
rc = 0;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
while(loop--) {
@@ -1230,7 +1230,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
notify alert from the server. No way to gsk_secure_soc_read() now, so
use read(). */
- nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf));
+ nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf));
if(nread < 0) {
char buffer[STRERROR_LEN];
@@ -1241,7 +1241,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
}
return rc;
diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h
index cf923f6b85..c71e6a0117 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.h
+++ b/Utilities/cmcurl/lib/vtls/gskit.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index 104dce6093..07dfaa437c 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -84,7 +84,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
{
struct Curl_cfilter *cf = s;
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
@@ -102,7 +102,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
{
struct Curl_cfilter *cf = s;
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
@@ -214,7 +214,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
gnutls_session_t session;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
DEBUGASSERT(backend);
session = backend->gtls.session;
@@ -434,12 +434,10 @@ CURLcode gtls_client_init(struct Curl_easy *data,
}
#ifdef USE_GNUTLS_SRP
- if((config->authtype == CURL_TLSAUTH_SRP) &&
- Curl_auth_allowed_to_host(data)) {
+ if(config->username && Curl_auth_allowed_to_host(data)) {
infof(data, "Using TLS-SRP username: %s", config->username);
- rc = gnutls_srp_allocate_client_credentials(
- &gtls->srp_client_cred);
+ rc = gnutls_srp_allocate_client_credentials(&gtls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
gnutls_strerror(rc));
@@ -581,7 +579,7 @@ CURLcode gtls_client_init(struct Curl_easy *data,
#ifdef USE_GNUTLS_SRP
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
- if(config->authtype == CURL_TLSAUTH_SRP) {
+ if(config->username) {
size_t len = strlen(prioritylist);
char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
@@ -646,7 +644,7 @@ CURLcode gtls_client_init(struct Curl_easy *data,
#ifdef USE_GNUTLS_SRP
/* put the credentials to the current session */
- if(config->authtype == CURL_TLSAUTH_SRP) {
+ if(config->username) {
rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP,
gtls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
@@ -700,32 +698,22 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(result)
return result;
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- gnutls_datum_t protocols[2];
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ gnutls_datum_t alpn[ALPN_ENTRIES_MAX];
+ size_t i;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur].data = (unsigned char *)ALPN_H2;
- protocols[cur].size = ALPN_H2_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (unsigned char *)connssl->alpn->entries[i];
+ alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]);
}
-#endif
-
- protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
- protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) {
+ if(gnutls_alpn_set_protocols(backend->gtls.session, alpn,
+ (unsigned)connssl->alpn->count, 0)) {
failf(data, "failed setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
/* This might be a reconnect, so we check for a session ID in the cache
@@ -860,10 +848,8 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
config->verifyhost ||
config->issuercert) {
#ifdef USE_GNUTLS_SRP
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
- && ssl_config->primary.username
- && !config->verifypeer
- && gnutls_cipher_get(session)) {
+ if(ssl_config->primary.username && !config->verifypeer &&
+ gnutls_cipher_get(session)) {
/* no peer cert, but auth is ok if we have SRP user and cipher and no
peer verify */
}
@@ -1271,28 +1257,10 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
int rc;
rc = gnutls_alpn_get_selected_protocol(session, &proto);
- if(rc == 0) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
- proto.data);
-
-#ifdef USE_HTTP2
- if(proto.size == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, proto.data,
- ALPN_H2_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(proto.size == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
+ if(rc == 0)
+ Curl_alpn_set_negotiated(cf, data, proto.data, proto.size);
else
- infof(data, VTLS_INFOF_NO_ALPN);
-
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
}
if(ssl_config->primary.sessionid) {
@@ -1516,7 +1484,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
char buf[120];
while(!done) {
- int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
/* Something to read, let's do it and hope that it is the close
@@ -1556,8 +1524,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
gnutls_certificate_free_credentials(backend->gtls.cred);
#ifdef USE_GNUTLS_SRP
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
- && ssl_config->primary.username != NULL)
+ if(ssl_config->primary.username)
gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
#endif
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h
index 49c1c47637..ac141e1c61 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.h
+++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c
index 2a648f20a9..e827dc58f3 100644
--- a/Utilities/cmcurl/lib/vtls/hostcheck.c
+++ b/Utilities/cmcurl/lib/vtls/hostcheck.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.h b/Utilities/cmcurl/lib/vtls/hostcheck.h
index d3c4eab56d..22a1ac2e56 100644
--- a/Utilities/cmcurl/lib/vtls/hostcheck.h
+++ b/Utilities/cmcurl/lib/vtls/hostcheck.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c
index 1952a690ca..d37bb183e7 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.c
+++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h
index 5d3c675b3e..eff5bf38f3 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.h
+++ b/Utilities/cmcurl/lib/vtls/keylog.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 0b81662b93..7f0f4e3668 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -159,15 +159,14 @@ static void mbed_debug(void *context, int level, const char *f_name,
static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
{
struct Curl_cfilter *cf = bio;
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
- blen, (int)nwritten, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d",
+ blen, nwritten, result));
if(nwritten < 0 && CURLE_AGAIN == result) {
nwritten = MBEDTLS_ERR_SSL_WANT_WRITE;
}
@@ -177,8 +176,7 @@ static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
{
struct Curl_cfilter *cf = bio;
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
@@ -188,8 +186,8 @@ static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
- blen, (int)nread, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d",
+ blen, nread, result));
if(nread < 0 && CURLE_AGAIN == result) {
nread = MBEDTLS_ERR_SSL_WANT_READ;
}
@@ -648,14 +646,13 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- const char **p = &backend->protocols[0];
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2)
- *p++ = ALPN_H2;
-#endif
- *p++ = ALPN_HTTP_1_1;
- *p = NULL;
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ size_t i;
+
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ backend->protocols[i] = connssl->alpn->entries[i];
+ }
/* this function doesn't clone the protocols array, which is why we need
to keep it around */
if(mbedtls_ssl_conf_alpn_protocols(&backend->config,
@@ -663,8 +660,8 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "Failed setting ALPN protocols");
return CURLE_SSL_CONNECT_ERROR;
}
- for(p = &backend->protocols[0]; *p; ++p)
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -844,28 +841,11 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
+ if(connssl->alpn) {
+ const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
- if(next_protocol) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol);
-#ifdef USE_HTTP2
- if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) &&
- !next_protocol[ALPN_H2_LENGTH]) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) &&
- !next_protocol[ALPN_HTTP_1_1_LENGTH]) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
- else {
- infof(data, VTLS_INFOF_NO_ALPN);
- }
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto,
+ proto? strlen(proto) : 0);
}
#endif
@@ -1081,7 +1061,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
{
CURLcode retcode;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.h b/Utilities/cmcurl/lib/vtls/mbedtls.h
index ec3b43bf9c..d8a0a06eb6 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.h
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
index 7d019ede53..bcb7106a63 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
index 22e8725ab0..2b0bd41c8b 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c
index 03694d22ba..774cbdd7ba 100644
--- a/Utilities/cmcurl/lib/vtls/nss.c
+++ b/Utilities/cmcurl/lib/vtls/nss.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -873,11 +873,11 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
#endif
case SSL_NEXT_PROTO_NO_SUPPORT:
case SSL_NEXT_PROTO_NO_OVERLAP:
- infof(data, VTLS_INFOF_NO_ALPN);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
return;
#ifdef SSL_ENABLE_ALPN
case SSL_NEXT_PROTO_SELECTED:
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf);
+ Curl_alpn_set_negotiated(cf, data, buf, buflen);
break;
#endif
default:
@@ -885,25 +885,6 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
break;
}
-#ifdef USE_HTTP2
- if(buflen == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(buflen == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
-
- /* This callback might get called when PR_Recv() is used within
- * close_one() during a connection shutdown. At that point there might not
- * be any "bundle" associated with the connection anymore.
- */
- if(conn->bundle)
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
}
@@ -1897,7 +1878,7 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
PRFileDesc *nspr_io_stub = NULL;
PRBool ssl_no_cache;
PRBool ssl_cbc_random_iv;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
@@ -2163,27 +2144,17 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
#endif
#if defined(SSL_ENABLE_ALPN)
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- unsigned char protocols[128];
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur++] = ALPN_H2_LENGTH;
- memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- }
-#endif
- protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
- if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess)
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len)
+ != SECSuccess) {
+ failf(data, "Error setting ALPN");
goto error;
+ }
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -2393,6 +2364,19 @@ static ssize_t nss_send(struct Curl_cfilter *cf,
return rc; /* number of bytes */
}
+static bool
+nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ PRFileDesc *fd = connssl->backend->handle->lower;
+ char buf;
+
+ (void) data;
+
+ /* Returns true in case of error to force reading. */
+ return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0;
+}
+
static ssize_t nss_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, /* transfer */
char *buf, /* store read data here */
@@ -2543,7 +2527,7 @@ const struct Curl_ssl Curl_ssl_nss = {
nss_check_cxn, /* check_cxn */
/* NSS has no shutdown function provided and thus always fail */
Curl_none_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
+ nss_data_pending, /* data_pending */
nss_random, /* random */
nss_cert_status_request, /* cert_status_request */
nss_connect, /* connect */
diff --git a/Utilities/cmcurl/lib/vtls/nssg.h b/Utilities/cmcurl/lib/vtls/nssg.h
index 454a38f1fb..ad7eef5801 100644
--- a/Utilities/cmcurl/lib/vtls/nssg.h
+++ b/Utilities/cmcurl/lib/vtls/nssg.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index c5085be3c3..e3a50bd6ef 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -96,6 +96,7 @@
#include "curl_memory.h"
#include "memdebug.h"
+
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -262,6 +263,12 @@
#define HAVE_OPENSSL_VERSION
#endif
+#ifdef OPENSSL_IS_BORINGSSL
+typedef uint32_t sslerr_t;
+#else
+typedef unsigned long sslerr_t;
+#endif
+
/*
* Whether the OpenSSL version has the API needed to support sharing an
* X509_STORE between connections. The API is:
@@ -285,17 +292,17 @@
#endif /* !LIBRESSL_VERSION_NUMBER */
struct ssl_backend_data {
- struct Curl_easy *logger; /* transfer handle to pass trace logs to, only
- using sockindex 0 */
/* these ones requires specific SSL-types */
SSL_CTX* ctx;
SSL* handle;
X509* server_cert;
+ BIO_METHOD *bio_method;
CURLcode io_result; /* result of last BIO cfilter operation */
#ifndef HAVE_KEYLOG_CALLBACK
/* Set to true once a valid keylog entry has been created to avoid dupes. */
bool keylog_done;
#endif
+ bool x509_store_setup; /* x509 store has been set up */
};
#if defined(HAVE_SSL_X509_STORE_SHARE)
@@ -710,14 +717,14 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result = CURLE_SEND_ERROR;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
- blen, (int)nwritten, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d",
+ blen, (int)nwritten, result));
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nwritten < 0) {
@@ -731,7 +738,7 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_RECV_ERROR;
@@ -741,64 +748,75 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
- blen, (int)nread, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d",
+ blen, (int)nread, result));
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nread < 0) {
if(CURLE_AGAIN == result)
BIO_set_retry_read(bio);
}
+
+ /* Before returning server replies to the SSL instance, we need
+ * to have setup the x509 store or verification will fail. */
+ if(!connssl->backend->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx);
+ if(result) {
+ connssl->backend->io_result = result;
+ return -1;
+ }
+ connssl->backend->x509_store_setup = TRUE;
+ }
+
return (int)nread;
}
-static BIO_METHOD *bio_cf_method = NULL;
-
#if USE_PRE_1_1_API
static BIO_METHOD bio_cf_meth_1_0 = {
- BIO_TYPE_MEM,
- "OpenSSL CF BIO",
- bio_cf_out_write,
- bio_cf_in_read,
- NULL, /* puts is never called */
- NULL, /* gets is never called */
- bio_cf_ctrl,
- bio_cf_create,
- bio_cf_destroy,
- NULL
+ BIO_TYPE_MEM,
+ "OpenSSL CF BIO",
+ bio_cf_out_write,
+ bio_cf_in_read,
+ NULL, /* puts is never called */
+ NULL, /* gets is never called */
+ bio_cf_ctrl,
+ bio_cf_create,
+ bio_cf_destroy,
+ NULL
};
-static void bio_cf_init_methods(void)
+static BIO_METHOD *bio_cf_method_create(void)
{
- bio_cf_method = &bio_cf_meth_1_0;
+ return &bio_cf_meth_1_0;
}
-#define bio_cf_free_methods() Curl_nop_stmt
+#define bio_cf_method_free(m) Curl_nop_stmt
#else
-static void bio_cf_init_methods(void)
+static BIO_METHOD *bio_cf_method_create(void)
{
- bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
- BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
- BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
- BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
- BIO_meth_set_create(bio_cf_method, &bio_cf_create);
- BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+ BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
+ if(m) {
+ BIO_meth_set_write(m, &bio_cf_out_write);
+ BIO_meth_set_read(m, &bio_cf_in_read);
+ BIO_meth_set_ctrl(m, &bio_cf_ctrl);
+ BIO_meth_set_create(m, &bio_cf_create);
+ BIO_meth_set_destroy(m, &bio_cf_destroy);
+ }
+ return m;
}
-static void bio_cf_free_methods(void)
+static void bio_cf_method_free(BIO_METHOD *m)
{
- BIO_meth_free(bio_cf_method);
+ if(m)
+ BIO_meth_free(m);
}
#endif
-static bool ossl_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-
/*
* Number of bytes to read from the random number seed file. This must be
* a finite value (because some entropy "files" like /dev/urandom have
@@ -930,54 +948,6 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
return buf;
}
-/* Return an extra data index for the transfer data.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_data_index(void)
-{
- static int ssl_ex_data_data_index = -1;
- if(ssl_ex_data_data_index < 0) {
- ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return ssl_ex_data_data_index;
-}
-
-/* Return an extra data index for the associated Curl_cfilter instance.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_cf_index(void)
-{
- static int ssl_ex_data_cf_index = -1;
- if(ssl_ex_data_cf_index < 0) {
- ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return ssl_ex_data_cf_index;
-}
-
-/* Return an extra data index for the sockindex.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_sockindex_index(void)
-{
- static int sockindex_index = -1;
- if(sockindex_index < 0) {
- sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return sockindex_index;
-}
-
-/* Return an extra data index for proxy boolean.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_proxy_index(void)
-{
- static int proxy_index = -1;
- if(proxy_index < 0) {
- proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return proxy_index;
-}
-
static int passwd_callback(char *buf, int num, int encrypting,
void *global_passwd)
{
@@ -1254,7 +1224,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
if(ret) {
X509 *ca;
- unsigned long err;
+ sslerr_t err;
if(!SSL_CTX_clear_chain_certs(ctx)) {
ret = 0;
@@ -1776,14 +1746,8 @@ static int ossl_init(void)
OpenSSL_add_all_algorithms();
#endif
- bio_cf_init_methods();
Curl_tls_keylog_open();
- /* Initialize the extra data indexes */
- if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 ||
- ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0)
- return 0;
-
return 1;
}
@@ -1822,7 +1786,6 @@ static void ossl_cleanup(void)
#endif
Curl_tls_keylog_close();
- bio_cf_free_methods();
}
/*
@@ -1840,7 +1803,10 @@ static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
#ifdef MSG_PEEK
char buf;
ssize_t nread;
- nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex],
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+ if(sock == CURL_SOCKET_BAD)
+ return 0; /* no socket, consider closed */
+ nread = recv((RECV_TYPE_ARG1)sock,
(RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1,
(RECV_TYPE_ARG4)MSG_PEEK);
if(nread == 0)
@@ -1968,19 +1934,15 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
return list;
}
-#define set_logger(connssl, data) \
- connssl->backend->logger = data
-
static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ (void)data;
DEBUGASSERT(backend);
if(backend->handle) {
- set_logger(connssl, data);
-
if(cf->next && cf->next->connected) {
char buf[32];
/* Maybe the server has already sent a close notify alert.
@@ -1997,6 +1959,11 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
if(backend->ctx) {
SSL_CTX_free(backend->ctx);
backend->ctx = NULL;
+ backend->x509_store_setup = FALSE;
+ }
+ if(backend->bio_method) {
+ bio_cf_method_free(backend->bio_method);
+ backend->bio_method = NULL;
}
}
@@ -2034,7 +2001,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
if(backend->handle) {
buffsize = (int)sizeof(buf);
while(!done && loop--) {
- int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
ERR_clear_error();
@@ -2163,6 +2130,22 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
return FALSE;
}
+static CURLcode
+ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert, const char *hostname,
+ const char *dispname);
+
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert)
+{
+ const char *hostname, *dispname;
+ int port;
+
+ (void)conn;
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
+ return ossl_verifyhost(data, conn, server_cert, hostname, dispname);
+}
+
/* Quote from RFC2818 section 3.1 "Server Identity"
If a subjectAltName extension of type dNSName is present, that MUST
@@ -2185,8 +2168,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
This function is now used from ngtcp2 (QUIC) as well.
*/
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- X509 *server_cert)
+static CURLcode
+ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert, const char *hostname,
+ const char *dispname)
{
bool matched = FALSE;
int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
@@ -2200,12 +2185,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
CURLcode result = CURLE_OK;
bool dNSName = FALSE; /* if a dNSName field exists in the cert */
bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */
- const char *hostname, *dispname;
- int port;
size_t hostlen;
(void)conn;
- Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
hostlen = strlen(hostname);
#ifndef ENABLE_IPV6
@@ -2668,24 +2650,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, SSL *ssl,
void *userp)
{
- char unknown[32];
- const char *verstr = NULL;
- struct connectdata *conn = userp;
- int cf_idx = ossl_get_ssl_cf_index();
- struct ssl_connect_data *connssl;
+ const char *verstr = "???";
+ struct Curl_cfilter *cf = userp;
struct Curl_easy *data = NULL;
- struct Curl_cfilter *cf;
-
- DEBUGASSERT(cf_idx >= 0);
- cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
- DEBUGASSERT(cf);
- connssl = cf->ctx;
- DEBUGASSERT(connssl);
- DEBUGASSERT(connssl->backend);
- data = connssl->backend->logger;
+ char unknown[32];
- if(!conn || !data || !data->set.fdebug ||
- (direction != 0 && direction != 1))
+ if(!cf)
+ return;
+ data = CF_DATA_CURRENT(cf);
+ if(!data || !data->set.fdebug || (direction && direction != 1))
return;
switch(ssl_ver) {
@@ -2730,6 +2703,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
* For TLS 1.3, skip notification of the decrypted inner Content-Type.
*/
if(ssl_ver
+#ifdef SSL3_RT_HEADER
+ && content_type != SSL3_RT_HEADER
+#endif
#ifdef SSL3_RT_INNER_CONTENT_TYPE
&& content_type != SSL3_RT_INNER_CONTENT_TYPE
#endif
@@ -2765,7 +2741,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
}
txt_len = msnprintf(ssl_buf, sizeof(ssl_buf),
- CFMSG(cf, "%s (%s), %s, %s (%d):\n"),
+ "%s (%s), %s, %s (%d):\n",
verstr, direction?"OUT":"IN",
tls_rt_name, msg_name, msg_type);
if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
@@ -2983,21 +2959,14 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
struct Curl_easy *data;
struct Curl_cfilter *cf;
const struct ssl_config_data *config;
- curl_socket_t *sockindex_ptr;
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
+ struct ssl_connect_data *connssl;
bool isproxy;
- if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0)
- return 0;
-
- cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
- data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx);
+ cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ connssl = cf? cf->ctx : NULL;
+ data = connssl? CF_DATA_CURRENT(cf) : NULL;
/* The sockindex has been stored as a pointer to an array element */
- sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
- if(!cf || !data || !sockindex_ptr)
+ if(!cf || !data)
return 0;
isproxy = Curl_ssl_cf_is_proxy(cf);
@@ -3091,7 +3060,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
BIO_free(cbio);
/* if we didn't end up importing anything, treat that as an error */
- return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE);
+ return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
}
static CURLcode populate_x509_store(struct Curl_cfilter *cf,
@@ -3110,206 +3079,219 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
const char * const ssl_crlfile = ssl_config->primary.CRLfile;
const bool verifypeer = conn_config->verifypeer;
bool imported_native_ca = false;
+ bool imported_ca_info_blob = false;
if(!store)
return CURLE_OUT_OF_MEMORY;
+ if(verifypeer) {
#if defined(USE_WIN32_CRYPTO)
- /* Import certificates from the Windows root certificate store if requested.
- https://stackoverflow.com/questions/9507184/
- https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
- https://datatracker.ietf.org/doc/html/rfc5280 */
- if((conn_config->verifypeer || conn_config->verifyhost) &&
- (ssl_config->native_ca_store)) {
- HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
-
- if(hStore) {
- PCCERT_CONTEXT pContext = NULL;
- /* The array of enhanced key usage OIDs will vary per certificate and is
- declared outside of the loop so that rather than malloc/free each
- iteration we can grow it with realloc, when necessary. */
- CERT_ENHKEY_USAGE *enhkey_usage = NULL;
- DWORD enhkey_usage_size = 0;
-
- /* This loop makes a best effort to import all valid certificates from
- the MS root store. If a certificate cannot be imported it is skipped.
- 'result' is used to store only hard-fail conditions (such as out of
- memory) that cause an early break. */
- result = CURLE_OK;
- for(;;) {
- X509 *x509;
- FILETIME now;
- BYTE key_usage[2];
- DWORD req_size;
- const unsigned char *encoded_cert;
+ /* Import certificates from the Windows root certificate store if
+ requested.
+ https://stackoverflow.com/questions/9507184/
+ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+ https://datatracker.ietf.org/doc/html/rfc5280 */
+ if(ssl_config->native_ca_store) {
+ HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and
+ is declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is
+ skipped. 'result' is used to store only hard-fail conditions (such
+ as out of memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char cert_name[256];
+ char cert_name[256];
#endif
- pContext = CertEnumCertificatesInStore(hStore, pContext);
- if(!pContext)
- break;
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
- NULL, cert_name, sizeof(cert_name))) {
- strcpy(cert_name, "Unknown");
- }
- infof(data, "SSL: Checking cert \"%s\"", cert_name);
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"", cert_name);
#endif
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
- encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
- if(!encoded_cert)
- continue;
-
- GetSystemTimeAsFileTime(&now);
- if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
- CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
- continue;
-
- /* If key usage exists check for signing attribute */
- if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
- pContext->pCertInfo,
- key_usage, sizeof(key_usage))) {
- if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
continue;
- }
- else if(GetLastError())
- continue;
-
- /* If enhanced key usage exists check for server auth attribute.
- *
- * Note "In a Microsoft environment, a certificate might also have EKU
- * extended properties that specify valid uses for the certificate."
- * The call below checks both, and behavior varies depending on what is
- * found. For more details see CertGetEnhancedKeyUsage doc.
- */
- if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
- if(req_size && req_size > enhkey_usage_size) {
- void *tmp = realloc(enhkey_usage, req_size);
-
- if(!tmp) {
- failf(data, "SSL: Out of memory allocating for OID list");
- result = CURLE_OUT_OF_MEMORY;
- break;
- }
- enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
- enhkey_usage_size = req_size;
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
}
+ else if(GetLastError())
+ continue;
- if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
- if(!enhkey_usage->cUsageIdentifier) {
- /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
- good for all uses. If it returns zero, the certificate has no
- valid uses." */
- if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
- continue;
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have
+ * EKU extended properties that specify valid uses for the
+ * certificate." The call below checks both, and behavior varies
+ * depending on what is found. For more details see
+ * CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
}
- else {
- DWORD i;
- bool found = false;
-
- for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
- if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
- enhkey_usage->rgpszUsageIdentifier[i])) {
- found = true;
- break;
- }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
+ is good for all uses. If it returns zero, the certificate
+ has no valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
}
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
- if(!found)
- continue;
+ if(!found)
+ continue;
+ }
}
+ else
+ continue;
}
else
continue;
- }
- else
- continue;
- x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
- if(!x509)
- continue;
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
- /* Try to import the certificate. This may fail for legitimate reasons
- such as duplicate certificate, which is allowed by MS but not
- OpenSSL. */
- if(X509_STORE_add_cert(store, x509) == 1) {
+ /* Try to import the certificate. This may fail for legitimate
+ reasons such as duplicate certificate, which is allowed by MS but
+ not OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- infof(data, "SSL: Imported cert \"%s\"", cert_name);
+ infof(data, "SSL: Imported cert \"%s\"", cert_name);
#endif
- imported_native_ca = true;
+ imported_native_ca = true;
+ }
+ X509_free(x509);
}
- X509_free(x509);
- }
- free(enhkey_usage);
- CertFreeCertificateContext(pContext);
- CertCloseStore(hStore, 0);
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
- if(result)
- return result;
+ if(result)
+ return result;
+ }
+ if(imported_native_ca)
+ infof(data, "successfully imported Windows CA store");
+ else
+ infof(data, "error importing Windows CA store, continuing anyway");
}
- if(imported_native_ca)
- infof(data, "successfully imported Windows CA store");
- else
- infof(data, "error importing Windows CA store, continuing anyway");
- }
#endif
-
- if(ca_info_blob) {
- result = load_cacert_from_memory(store, ca_info_blob);
- if(result) {
- if(result == CURLE_OUT_OF_MEMORY ||
- (verifypeer && !imported_native_ca)) {
+ if(ca_info_blob) {
+ result = load_cacert_from_memory(store, ca_info_blob);
+ if(result) {
failf(data, "error importing CA certificate blob");
return result;
}
- /* Only warn if no certificate verification is required. */
- infof(data, "error importing CA certificate blob, continuing anyway");
+ else {
+ imported_ca_info_blob = true;
+ infof(data, "successfully imported CA certificate blob");
+ }
}
- }
- if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
+ if(ssl_cafile || ssl_capath) {
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
- /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
- if(ssl_cafile &&
- !X509_STORE_load_file(store, ssl_cafile)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate file: %s", ssl_cafile);
- return CURLE_SSL_CACERT_BADFILE;
- }
- if(ssl_capath &&
- !X509_STORE_load_path(store, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate path: %s", ssl_capath);
- return CURLE_SSL_CACERT_BADFILE;
- }
+ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
+ if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "error setting certificate file, continuing anyway");
+ }
+ if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "error setting certificate path, continuing anyway");
+ }
#else
- /* tell OpenSSL where to find CA certificates that are used to verify the
- server's certificate. */
- if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return CURLE_SSL_CACERT_BADFILE;
- }
+ /* tell OpenSSL where to find CA certificates that are used to verify the
+ server's certificate. */
+ if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway");
+ }
+ }
#endif
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
#ifdef CURL_CA_FALLBACK
- if(verifypeer &&
- !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
- /* verifying the peer without any CA certificates won't
- work so use openssl's built-in default as fallback */
- X509_STORE_set_default_paths(store);
- }
+ if(!ssl_cafile && !ssl_capath &&
+ !imported_native_ca && !imported_ca_info_blob) {
+ /* verifying the peer without any CA certificates won't
+ work so use openssl's built-in default as fallback */
+ X509_STORE_set_default_paths(store);
+ }
#endif
+ }
if(ssl_crlfile) {
/* tell OpenSSL where to find CRL file that is used to check certificate
@@ -3440,9 +3422,9 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
}
}
-static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_backend_data *backend)
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -3462,10 +3444,10 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
cached_store = get_cached_x509_store(cf, data);
if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
- SSL_CTX_set_cert_store(backend->ctx, cached_store);
+ SSL_CTX_set_cert_store(ssl_ctx, cached_store);
}
else {
- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
result = populate_x509_store(cf, data, store);
if(result == CURLE_OK && cache_criteria_met) {
@@ -3476,11 +3458,11 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
return result;
}
#else /* HAVE_SSL_X509_STORE_SHARE */
-static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_backend_data *backend)
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx)
{
- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
return populate_x509_store(cf, data, store);
}
@@ -3510,9 +3492,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
#endif
#endif
const long int ssl_version = conn_config->version;
-#ifdef USE_OPENSSL_SRP
- const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype;
-#endif
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;
@@ -3580,8 +3559,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
if(data->set.fdebug && data->set.verbose) {
/* the SSL trace callback is only used for verbose logging */
SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
- SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn);
- set_logger(connssl, data);
+ SSL_CTX_set_msg_callback_arg(backend->ctx, cf);
}
#endif
@@ -3677,36 +3655,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
SSL_CTX_set_options(backend->ctx, ctx_options);
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- unsigned char protocols[128];
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur++] = ALPN_H2_LENGTH;
-
- memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- }
-#endif
-
- protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
- /* expects length prefixed preference ordered list of protocols in wire
- * format
- */
- if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) {
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result ||
+ SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) {
failf(data, "Error setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -3764,8 +3723,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
#endif
#ifdef USE_OPENSSL_SRP
- if((ssl_authtype == CURL_TLSAUTH_SRP) &&
- Curl_auth_allowed_to_host(data)) {
+ if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) {
char * const ssl_username = ssl_config->primary.username;
char * const ssl_password = ssl_config->primary.password;
infof(data, "Using TLS-SRP username: %s", ssl_username);
@@ -3789,10 +3747,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
#endif
- result = set_up_x509_store(cf, data, backend);
- if(result)
- return result;
-
/* OpenSSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
@@ -3836,6 +3790,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OUT_OF_MEMORY;
}
+ SSL_set_app_data(backend->handle, cf);
+
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus)
@@ -3863,13 +3819,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
#endif
- if(!ossl_attach_data(cf, data)) {
- /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */
- failf(data, "SSL: ossl_attach_data failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
+ SSL_set_app_data(backend->handle, cf);
if(ssl_config->primary.sessionid) {
Curl_ssl_sessionid_lock(data);
@@ -3888,13 +3838,26 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
Curl_ssl_sessionid_unlock(data);
}
- bio = BIO_new(bio_cf_method);
+ backend->bio_method = bio_cf_method_create();
+ if(!backend->bio_method)
+ return CURLE_OUT_OF_MEMORY;
+ bio = BIO_new(backend->bio_method);
if(!bio)
return CURLE_OUT_OF_MEMORY;
BIO_set_data(bio, cf);
+#ifdef HAVE_SSL_SET0_WBIO
+ /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works
+ * without backward compat quirks. Every call takes one reference, so we
+ * up it and pass. SSL* then owns it and will free.
+ * We check on the function in configure, since libressl and friends
+ * each have their own versions to add support for this. */
+ BIO_up_ref(bio);
+ SSL_set0_rbio(backend->handle, bio);
+ SSL_set0_wbio(backend->handle, bio);
+#else
SSL_set_bio(backend->handle, bio, bio);
-
+#endif
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
@@ -3915,6 +3878,16 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
ERR_clear_error();
err = SSL_connect(backend->handle);
+
+ if(!backend->x509_store_setup) {
+ /* After having send off the ClientHello, we prepare the x509
+ * store to verify the coming certificate from the server */
+ CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+ if(result)
+ return result;
+ backend->x509_store_setup = TRUE;
+ }
+
#ifndef HAVE_KEYLOG_CALLBACK
if(Curl_tls_keylog_enabled()) {
/* If key logging is enabled, wait for the handshake to complete and then
@@ -3949,7 +3922,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
}
else {
/* untreated error */
- unsigned long errdetail;
+ sslerr_t errdetail;
char error_buffer[256]="";
CURLcode result;
long lerr;
@@ -4043,26 +4016,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
const unsigned char *neg_protocol;
unsigned int len;
SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
- if(len) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol);
-
-#ifdef USE_HTTP2
- if(len == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, neg_protocol, len)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(len == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
- else
- infof(data, VTLS_INFOF_NO_ALPN);
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ return Curl_alpn_set_negotiated(cf, data, neg_protocol, len);
}
#endif
@@ -4205,7 +4160,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
BIO_free(mem);
if(conn_config->verifyhost) {
- result = Curl_ossl_verifyhost(data, conn, backend->server_cert);
+ result = ossl_verifyhost(data, conn, backend->server_cert,
+ connssl->hostname, connssl->dispname);
if(result) {
X509_free(backend->server_cert);
backend->server_cert = NULL;
@@ -4379,7 +4335,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
{
CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
@@ -4418,8 +4374,9 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
}
/* if ssl is expecting something, check if it's available. */
- if(connssl->connecting_state == ssl_connect_2_reading ||
- connssl->connecting_state == ssl_connect_2_writing) {
+ if(!nonblocking &&
+ (connssl->connecting_state == ssl_connect_2_reading ||
+ connssl->connecting_state == ssl_connect_2_writing)) {
curl_socket_t writefd = ssl_connect_2_writing ==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
@@ -4427,7 +4384,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking?0:timeout_ms);
+ timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -4435,11 +4392,6 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
goto out;
}
if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- result = CURLE_OK;
- goto out;
- }
/* timeout */
failf(data, "SSL connection timeout");
result = CURLE_OPERATION_TIMEDOUT;
@@ -4527,7 +4479,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
'size_t' */
int err;
char error_buffer[256];
- unsigned long sslerror;
+ sslerr_t sslerror;
int memlen;
int rc;
struct ssl_connect_data *connssl = cf->ctx;
@@ -4539,7 +4491,6 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- set_logger(connssl, data);
rc = SSL_write(backend->handle, mem, memlen);
if(rc <= 0) {
@@ -4636,7 +4587,6 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- set_logger(connssl, data);
nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
if(nread <= 0) {
@@ -4854,89 +4804,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
(void *)backend->ctx : (void *)backend->handle;
}
-static bool ossl_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- const struct ssl_config_data *config;
-
- DEBUGASSERT(backend);
-
- /* If we don't have SSL context, do nothing. */
- if(!backend->handle)
- return FALSE;
-
- config = Curl_ssl_cf_get_config(cf, data);
- if(config->primary.sessionid) {
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
-
- if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
- proxy_idx >= 0) {
- int data_status, cf_status, sockindex_status, proxy_status;
-
- /* Store the data needed for the "new session" callback.
- * The sockindex is stored as a pointer to an array element. */
- data_status = SSL_set_ex_data(backend->handle, data_idx, data);
- cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf);
- sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx,
- cf->conn->sock + cf->sockindex);
-#ifndef CURL_DISABLE_PROXY
- proxy_status = SSL_set_ex_data(backend->handle, proxy_idx,
- Curl_ssl_cf_is_proxy(cf)?
- (void *) 1 : NULL);
-#else
- proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL);
-#endif
- if(data_status && cf_status && sockindex_status && proxy_status)
- return TRUE;
- }
- return FALSE;
- }
- return TRUE;
-}
-
-/*
- * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
- * the handshake. If the transfer that sets up the callback gets killed before
- * this callback arrives, we must make sure to properly clear the data to
- * avoid UAF problems. A future optimization could be to instead store another
- * transfer that might still be using the same connection.
- */
-
-static void ossl_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- DEBUGASSERT(backend);
-
- /* If we don't have SSL context, do nothing. */
- if(!backend->handle)
- return;
-
- if(ssl_config->primary.sessionid) {
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
-
- if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
- proxy_idx >= 0) {
- /* Disable references to data in "new session" callback to avoid
- * accessing a stale pointer. */
- SSL_set_ex_data(backend->handle, data_idx, NULL);
- SSL_set_ex_data(backend->handle, cf_idx, NULL);
- SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
- SSL_set_ex_data(backend->handle, proxy_idx, NULL);
- }
- }
-}
-
static void ossl_free_multi_ssl_backend_data(
struct multi_ssl_backend_data *mbackend)
{
@@ -4990,8 +4857,8 @@ const struct Curl_ssl Curl_ssl_openssl = {
#else
NULL, /* sha256sum */
#endif
- ossl_attach_data, /* use of data in this connection */
- ossl_detach_data, /* remote of data from this connection */
+ NULL, /* use of data in this connection */
+ NULL, /* remote of data from this connection */
ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h
index 9df4ecddba..950faab889 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.h
+++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -56,5 +56,14 @@ CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
+/**
+ * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and
+ * easy handle `data`. Will allow reuse of a shared cache if suitable
+ * and configured.
+ */
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx);
+
#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index 27f4ec8d8c..003533dbb9 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
@@ -150,6 +150,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t plain_bytes_copied = 0;
rustls_result rresult = 0;
char errorbuf[255];
+ size_t errorlen;
rustls_io_result io_error;
DEBUGASSERT(backend);
@@ -161,7 +162,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx,
&tls_bytes_read);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
- infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
+ DEBUGF(LOG_CF(data, cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
}
else if(io_error) {
char buffer[STRERROR_LEN];
@@ -171,12 +172,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return -1;
}
- infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read);
+ DEBUGF(LOG_CF(data, cf, "cr_recv: read %ld TLS bytes", tls_bytes_read));
rresult = rustls_connection_process_new_packets(rconn);
if(rresult != RUSTLS_RESULT_OK) {
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
- failf(data, "%.*s", n, errorbuf);
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_process_new_packets: %.*s",
+ errorlen, errorbuf);
*err = map_error(rresult);
return -1;
}
@@ -189,14 +191,21 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
plainlen - plain_bytes_copied,
&n);
if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
- infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. "
- "will try again later."));
+ DEBUGF(LOG_CF(data, cf, "cr_recv: got PLAINTEXT_EMPTY. "
+ "will try again later."));
backend->data_pending = FALSE;
break;
}
+ else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) {
+ failf(data, "rustls: peer closed TCP connection "
+ "without first closing TLS connection");
+ *err = CURLE_READ_ERROR;
+ return -1;
+ }
else if(rresult != RUSTLS_RESULT_OK) {
/* n always equals 0 in this case, don't need to check it */
- failf(data, "error in rustls_connection_read: %d", rresult);
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf);
*err = CURLE_READ_ERROR;
return -1;
}
@@ -207,7 +216,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
break;
}
else {
- infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n);
+ DEBUGF(LOG_CF(data, cf, "cr_recv: got %ld plain bytes", n));
plain_bytes_copied += n;
}
}
@@ -254,22 +263,25 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t tlswritten_total = 0;
rustls_result rresult;
rustls_io_result io_error;
+ char errorbuf[256];
+ size_t errorlen;
DEBUGASSERT(backend);
rconn = backend->conn;
- infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen);
+ DEBUGF(LOG_CF(data, cf, "cr_send: %ld plain bytes", plainlen));
if(plainlen > 0) {
rresult = rustls_connection_write(rconn, plainbuf, plainlen,
&plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
- failf(data, "error in rustls_connection_write");
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf);
*err = CURLE_WRITE_ERROR;
return -1;
}
else if(plainwritten == 0) {
- failf(data, "EOF in rustls_connection_write");
+ failf(data, "rustls_connection_write: EOF");
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -282,8 +294,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
&tlswritten);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
- infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"),
- tlswritten_total);
+ DEBUGF(LOG_CF(data, cf, "cr_send: EAGAIN after %zu bytes",
+ tlswritten_total));
*err = CURLE_AGAIN;
return -1;
}
@@ -299,7 +311,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
*err = CURLE_WRITE_ERROR;
return -1;
}
- infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten);
+ DEBUGF(LOG_CF(data, cf, "cr_send: wrote %zu TLS bytes", tlswritten));
tlswritten_total += tlswritten;
}
@@ -349,22 +361,25 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
char errorbuf[256];
size_t errorlen;
int result;
- rustls_slice_bytes alpn[2] = {
- { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH },
- { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
- };
DEBUGASSERT(backend);
rconn = backend->conn;
config_builder = rustls_client_config_builder_new();
-#ifdef USE_HTTP2
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
-#else
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
-#endif
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+ size_t i;
+
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+ alpn[i].len = strlen(connssl->alpn->entries[i]);
+ }
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ }
if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none);
@@ -384,7 +399,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
ca_info_blob->len, verifypeer);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to parse trusted certificates from blob");
+ failf(data, "rustls: failed to parse trusted certificates from blob");
rustls_root_cert_store_free(roots);
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
@@ -394,7 +409,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_client_config_builder_use_roots(config_builder, roots);
rustls_root_cert_store_free(roots);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to load trusted certificates");
+ failf(data, "rustls: failed to load trusted certificates");
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
return CURLE_SSL_CACERT_BADFILE;
@@ -404,7 +419,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_client_config_builder_load_roots_from_file(
config_builder, ssl_cafile);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to load trusted certificates");
+ failf(data, "rustls: failed to load trusted certificates");
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
return CURLE_SSL_CACERT_BADFILE;
@@ -416,7 +431,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
{
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost) {
- failf(data, "Failed to set SNI");
+ failf(data, "rustls: failed to get SNI");
return CURLE_SSL_CONNECT_ERROR;
}
result = rustls_client_connection_new(backend->config, snihost, &rconn);
@@ -439,29 +454,7 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t len = 0;
rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
- if(!protocol) {
- infof(data, VTLS_INFOF_NO_ALPN);
- return;
- }
-
-#ifdef USE_HTTP2
- if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(len == ALPN_HTTP_1_1_LENGTH &&
- 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1);
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- else {
- infof(data, "ALPN, negotiated an unrecognized protocol");
- }
-
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, protocol, len);
}
static CURLcode
@@ -469,7 +462,7 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_backend_data *const backend = connssl->backend;
struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK;
@@ -573,7 +566,7 @@ cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
curl_socket_t *socks)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_backend_data *const backend = connssl->backend;
struct rustls_connection *rconn = NULL;
@@ -616,7 +609,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
rustls_connection_send_close_notify(backend->conn);
n = cr_send(cf, data, NULL, 0, &tmperr);
if(n < 0) {
- failf(data, "error sending close notify: %d", tmperr);
+ failf(data, "rustls: error sending close_notify: %d", tmperr);
}
rustls_connection_free(backend->conn);
diff --git a/Utilities/cmcurl/lib/vtls/rustls.h b/Utilities/cmcurl/lib/vtls/rustls.h
index 6b393dd639..bfbe23de3e 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.h
+++ b/Utilities/cmcurl/lib/vtls/rustls.h
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 7eab9542af..452fa409a5 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -5,9 +5,9 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
- * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -1105,7 +1105,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#ifdef HAS_ALPN
/* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
Also it doesn't seem to be supported for Wine, see curl bug #983. */
- backend->use_alpn = cf->conn->bits.tls_enable_alpn &&
+ backend->use_alpn = connssl->alpn &&
!GetProcAddress(GetModuleHandle(TEXT("ntdll")),
"wine_get_version") &&
curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
@@ -1196,6 +1196,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
int list_start_index = 0;
unsigned int *extension_len = NULL;
unsigned short* list_len = NULL;
+ struct alpn_proto_buf proto;
/* The first four bytes will be an unsigned int indicating number
of bytes of data in the rest of the buffer. */
@@ -1215,25 +1216,22 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
list_start_index = cur;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
- alpn_buffer[cur++] = ALPN_H2_LENGTH;
- memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result) {
+ failf(data, "Error setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
}
-#endif
-
- alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ memcpy(&alpn_buffer[cur], proto.data, proto.len);
+ cur += proto.len;
*list_len = curlx_uitous(cur - list_start_index);
*extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
else {
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
@@ -1727,40 +1725,23 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
if(alpn_result.ProtoNegoStatus ==
SecApplicationProtocolNegotiationStatus_Success) {
- unsigned char alpn = 0;
-
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
- alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
+ unsigned char prev_alpn = cf->conn->alpn;
-#ifdef USE_HTTP2
- if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
- alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
- ALPN_HTTP_1_1_LENGTH)) {
- alpn = CURL_HTTP_VERSION_1_1;
- }
+ Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId,
+ alpn_result.ProtocolIdSize);
if(backend->recv_renegotiating) {
- if(alpn != cf->conn->alpn) {
+ if(prev_alpn != cf->conn->alpn &&
+ prev_alpn != CURL_HTTP_VERSION_NONE) {
+ /* Renegotiation selected a different protocol now, we cannot
+ * deal with this */
failf(data, "schannel: server selected an ALPN protocol too late");
return CURLE_SSL_CONNECT_ERROR;
}
}
- else
- cf->conn->alpn = alpn;
}
else {
if(!backend->recv_renegotiating)
- infof(data, VTLS_INFOF_NO_ALPN);
- }
-
- if(!backend->recv_renegotiating) {
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
}
}
#endif
@@ -1841,7 +1822,7 @@ schannel_connect_common(struct Curl_cfilter *cf,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
@@ -2056,7 +2037,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else if(!timeout_ms)
timeout_ms = TIMEDIFF_T_MAX;
- what = SOCKET_WRITABLE(cf->conn->sock[cf->sockindex], timeout_ms);
+ what = SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h
index 6d4235a96c..7fae39fa0a 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.h
+++ b/Utilities/cmcurl/lib/vtls/schannel.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -179,8 +179,8 @@ struct ssl_backend_data {
size_t encdata_offset, decdata_offset;
unsigned char *encdata_buffer, *decdata_buffer;
/* encdata_is_incomplete: if encdata contains only a partial record that
- can't be decrypted without another Curl_read_plain (that is, status is
- SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+ can't be decrypted without another recv() (that is, status is
+ SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
more bytes into encdata then set this back to false. */
bool encdata_is_incomplete;
unsigned long req_flags, ret_flags;
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index e4992162e6..d75ee8dfe7 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -5,9 +5,9 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
- * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index d903c53d10..2e98169e26 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -137,14 +137,6 @@
#include "memdebug.h"
-#define DEBUG_CF 0
-
-#if DEBUG_CF
-#define CF_DEBUGF(x) x
-#else
-#define CF_DEBUGF(x) do { } while(0)
-#endif
-
/* From MacTypes.h (which we can't include because it isn't present in iOS: */
#define ioErr -36
#define paramErr -50
@@ -840,15 +832,15 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection,
struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
OSStatus rtn = noErr;
DEBUGASSERT(data);
nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result);
- CF_DEBUGF(infof(data, CFMSG(cf, "bio_read(len=%zu) -> %zd, result=%d"),
- *dataLength, nread, result));
+ DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d",
+ *dataLength, nread, result));
if(nread < 0) {
switch(result) {
case CURLE_OK:
@@ -876,15 +868,15 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection,
struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
OSStatus rtn = noErr;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
- CF_DEBUGF(infof(data, CFMSG(cf, "bio_send(len=%zu) -> %zd, result=%d"),
- *dataLength, nwritten, result));
+ DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d",
+ *dataLength, nwritten, result));
if(nwritten <= 0) {
if(result == CURLE_AGAIN) {
rtn = errSSLWouldBlock;
@@ -1644,7 +1636,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
const bool verifypeer = conn_config->verifypeer;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
- bool isproxy = Curl_ssl_cf_is_proxy(cf);
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
@@ -1657,7 +1648,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step1")));
+ DEBUGF(LOG_CF(data, cf, "connect_step1"));
GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
#endif /* CURL_BUILD_MAC */
@@ -1805,33 +1796,28 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
- if(cf->conn->bits.tls_enable_alpn) {
+ if(connssl->alpn) {
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ struct alpn_proto_buf proto;
+ size_t i;
+ CFStringRef cstr;
CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks);
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!isproxy || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2));
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i],
+ kCFStringEncodingUTF8);
+ if(!cstr)
+ return CURLE_OUT_OF_MEMORY;
+ CFArrayAppendValue(alpnArr, cstr);
+ CFRelease(cstr);
}
-#endif
-
- CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- /* expects length prefixed preference ordered list of protocols in wire
- * format
- */
err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr);
if(err != noErr)
infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d",
err);
CFRelease(alpnArr);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
}
#endif
@@ -2302,7 +2288,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
/* This is not a PEM file, probably a certificate in DER format. */
rc = append_cert_to_array(data, certbuf, buflen, array);
if(rc != CURLE_OK) {
- CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed")));
+ DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
result = rc;
goto out;
}
@@ -2316,7 +2302,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
rc = append_cert_to_array(data, der, derlen, array);
free(der);
if(rc != CURLE_OK) {
- CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed")));
+ DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
result = rc;
goto out;
}
@@ -2332,7 +2318,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
goto out;
}
- CF_DEBUGF(infof(data, CFMSG(cf, "setting %d trust anchors"), n));
+ DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n));
ret = SecTrustSetAnchorCertificates(trust, array);
if(ret != noErr) {
failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret);
@@ -2354,11 +2340,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
switch(trust_eval) {
case kSecTrustResultUnspecified:
/* what does this really mean? */
- CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Unspecified")));
+ DEBUGF(LOG_CF(data, cf, "trust result: Unspecified"));
result = CURLE_OK;
goto out;
case kSecTrustResultProceed:
- CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Proceed")));
+ DEBUGF(LOG_CF(data, cf, "trust result: Proceed"));
result = CURLE_OK;
goto out;
@@ -2391,7 +2377,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
size_t buflen;
if(ca_info_blob) {
- CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from config blob")));
+ DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob"));
certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
if(!certbuf) {
return CURLE_OUT_OF_MEMORY;
@@ -2401,8 +2387,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
certbuf[ca_info_blob->len]='\0';
}
else if(cafile) {
- CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from file '%s'"),
- cafile));
+ DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile));
if(read_cert(cafile, &certbuf, &buflen) < 0) {
failf(data, "SSL: failed to read or invalid CA certificate");
return CURLE_SSL_CACERT_BADFILE;
@@ -2538,7 +2523,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
DEBUGASSERT(backend);
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step2")));
+ DEBUGF(LOG_CF(data, cf, "connect_step2"));
/* Here goes nothing: */
check_handshake:
@@ -3003,7 +2988,7 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
{
struct ssl_connect_data *connssl = cf->ctx;
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step3")));
+ DEBUGF(LOG_CF(data, cf, "connect_step3"));
/* There is no step 3!
* Well, okay, let's collect server certificates, and if verbose mode is on,
* let's print the details of the server certificates. */
@@ -3022,7 +3007,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
@@ -3112,7 +3097,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(ssl_connect_done == connssl->connecting_state) {
- CF_DEBUGF(infof(data, CFMSG(cf, "connected")));
+ DEBUGF(LOG_CF(data, cf, "connected"));
connssl->state = ssl_connection_complete;
*done = TRUE;
}
@@ -3158,7 +3143,7 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGASSERT(backend);
if(backend->ssl_ctx) {
- CF_DEBUGF(infof(data, CFMSG(cf, "close")));
+ DEBUGF(LOG_CF(data, cf, "close"));
(void)SSLClose(backend->ssl_ctx);
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
if(SSLCreateContext)
@@ -3200,9 +3185,10 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
rc = 0;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
+ SSL_SHUTDOWN_TIMEOUT);
- CF_DEBUGF(infof(data, CFMSG(cf, "shutdown")));
+ DEBUGF(LOG_CF(data, cf, "shutdown"));
while(loop--) {
if(what < 0) {
/* anything that gets here is fatally bad */
@@ -3229,7 +3215,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
}
return rc;
@@ -3271,7 +3257,7 @@ static int sectransp_check_cxn(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ssl_ctx) {
- CF_DEBUGF(infof(data, CFMSG(cf, "check connection")));
+ DEBUGF(LOG_CF(data, cf, "check connection"));
err = SSLGetSessionState(backend->ssl_ctx, &state);
if(err == noErr)
return state == kSSLConnected || state == kSSLHandshake;
@@ -3292,7 +3278,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ssl_ctx) { /* SSL is in use */
- CF_DEBUGF(infof(data, CFMSG(cf, "data_pending")));
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
if(err == noErr)
return buffer > 0UL;
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.h b/Utilities/cmcurl/lib/vtls/sectransp.h
index 2d53b7c480..0f1085ad91 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.h
+++ b/Utilities/cmcurl/lib/vtls/sectransp.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 873ee6bac7..15f6844c7b 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,6 +73,7 @@
#include "curl_memory.h"
#include "memdebug.h"
+
/* convenience macro to check if this handle is using a shared SSL session */
#define SSLSESSION_SHARED(data) (data->share && \
(data->share->specifier & \
@@ -150,7 +151,6 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
#ifdef USE_TLS_SRP
!Curl_timestrcmp(data->username, needle->username) &&
!Curl_timestrcmp(data->password, needle->password) &&
- (data->authtype == needle->authtype) &&
#endif
strcasecompare(data->cipher_list, needle->cipher_list) &&
strcasecompare(data->cipher_list13, needle->cipher_list13) &&
@@ -173,9 +173,6 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
dest->verifystatus = source->verifystatus;
dest->sessionid = source->sessionid;
dest->ssl_options = source->ssl_options;
-#ifdef USE_TLS_SRP
- dest->authtype = source->authtype;
-#endif
CLONE_BLOB(cert_blob);
CLONE_BLOB(ca_info_blob);
@@ -272,8 +269,8 @@ void Curl_ssl_cleanup(void)
static bool ssl_prefs_check(struct Curl_easy *data)
{
/* check for CURLOPT_SSLVERSION invalid parameter value */
- const long sslver = data->set.ssl.primary.version;
- if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) {
+ const unsigned char sslver = data->set.ssl.primary.version;
+ if(sslver >= CURL_SSLVERSION_LAST) {
failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
return FALSE;
}
@@ -293,7 +290,8 @@ static bool ssl_prefs_check(struct Curl_easy *data)
return TRUE;
}
-static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
+static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
+ const struct alpn_spec *alpn)
{
struct ssl_connect_data *ctx;
@@ -302,6 +300,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
if(!ctx)
return NULL;
+ ctx->alpn = alpn;
ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
@@ -318,13 +317,6 @@ static void cf_ctx_free(struct ssl_connect_data *ctx)
}
}
-static void cf_ctx_set_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- if(cf->ctx)
- ((struct ssl_connect_data *)cf->ctx)->call_data = data;
-}
-
static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
@@ -339,7 +331,6 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
result = Curl_ssl->connect_blocking(cf, data);
if(!result) {
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
DEBUGASSERT(connssl->state == ssl_connection_complete);
}
@@ -615,19 +606,20 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
curl_socket_t *socks)
{
struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
- (void)data;
- if(connssl->connecting_state == ssl_connect_2_writing) {
- /* write mode */
- socks[0] = cf->conn->sock[FIRSTSOCKET];
- return GETSOCK_WRITESOCK(0);
- }
- if(connssl->connecting_state == ssl_connect_2_reading) {
- /* read mode */
- socks[0] = cf->conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0);
+ if(sock != CURL_SOCKET_BAD) {
+ if(connssl->connecting_state == ssl_connect_2_writing) {
+ /* write mode */
+ socks[0] = sock;
+ return GETSOCK_WRITESOCK(0);
+ }
+ if(connssl->connecting_state == ssl_connect_2_reading) {
+ /* read mode */
+ socks[0] = sock;
+ return GETSOCK_READSOCK(0);
+ }
}
-
return GETSOCK_BLANK;
}
@@ -685,20 +677,6 @@ void Curl_ssl_version(char *buffer, size_t size)
#endif
}
-/*
- * This function tries to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn)
-{
- struct Curl_cfilter *cf = Curl_ssl_cf_get_ssl(conn->cfilter[FIRSTSOCKET]);
- return cf? Curl_ssl->check_cxn(cf, data) : -1;
-}
-
void Curl_ssl_free_certinfo(struct Curl_easy *data)
{
struct curl_certinfo *ci = &data->info.certs;
@@ -1430,53 +1408,80 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
#ifdef USE_SSL
+static void free_hostname(struct ssl_connect_data *connssl)
+{
+ if(connssl->dispname != connssl->hostname)
+ free(connssl->dispname);
+ free(connssl->hostname);
+ connssl->hostname = connssl->dispname = NULL;
+}
+
static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- /* TODO: close_one closes BOTH conn->ssl AND conn->proxy_ssl for this
- * sockindex (if in use). Gladly, it is safe to call more than once. */
if(connssl) {
Curl_ssl->close(cf, data);
connssl->state = ssl_connection_none;
+ free_hostname(connssl);
}
cf->connected = FALSE;
}
-static void reinit_hostname(struct Curl_cfilter *cf)
+static CURLcode reinit_hostname(struct Curl_cfilter *cf)
{
struct ssl_connect_data *connssl = cf->ctx;
+ const char *ehostname, *edispname;
+ int eport;
+ /* We need the hostname for SNI negotiation. Once handshaked, this
+ * remains the SNI hostname for the TLS connection. But when the
+ * connection is reused, the settings in cf->conn might change.
+ * So we keep a copy of the hostname we use for SNI.
+ */
#ifndef CURL_DISABLE_PROXY
if(Curl_ssl_cf_is_proxy(cf)) {
- /* TODO: there is not definition for a proxy setup on a secondary conn */
- connssl->hostname = cf->conn->http_proxy.host.name;
- connssl->dispname = cf->conn->http_proxy.host.dispname;
- connssl->port = cf->conn->http_proxy.port;
+ ehostname = cf->conn->http_proxy.host.name;
+ edispname = cf->conn->http_proxy.host.dispname;
+ eport = cf->conn->http_proxy.port;
}
else
#endif
{
- /* TODO: secondaryhostname is set to the IP address we connect to
- * in the FTP handler, it is assumed that host verification uses the
- * hostname from FIRSTSOCKET */
- if(cf->sockindex == SECONDARYSOCKET && 0) {
- connssl->hostname = cf->conn->secondaryhostname;
- connssl->dispname = connssl->hostname;
- connssl->port = cf->conn->secondary_port;
+ ehostname = cf->conn->host.name;
+ edispname = cf->conn->host.dispname;
+ eport = cf->conn->remote_port;
+ }
+
+ /* change if ehostname changed */
+ if(ehostname && (!connssl->hostname
+ || strcmp(ehostname, connssl->hostname))) {
+ free_hostname(connssl);
+ connssl->hostname = strdup(ehostname);
+ if(!connssl->hostname) {
+ free_hostname(connssl);
+ return CURLE_OUT_OF_MEMORY;
}
+ if(!edispname || !strcmp(ehostname, edispname))
+ connssl->dispname = connssl->hostname;
else {
- connssl->hostname = cf->conn->host.name;
- connssl->dispname = cf->conn->host.dispname;
- connssl->port = cf->conn->remote_port;
+ connssl->dispname = strdup(edispname);
+ if(!connssl->dispname) {
+ free_hostname(connssl);
+ return CURLE_OUT_OF_MEMORY;
+ }
}
}
- DEBUGASSERT(connssl->hostname);
+ connssl->port = eport;
+ return CURLE_OK;
}
static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
cf_close(cf, data);
+ CF_DATA_RESTORE(cf, save);
cf_ctx_free(cf->ctx);
cf->ctx = NULL;
}
@@ -1484,10 +1489,12 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
static void ssl_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
cf_close(cf, data);
cf->next->cft->close(cf->next, data);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
}
static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
@@ -1495,6 +1502,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
bool blocking, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
+ struct cf_call_data save;
CURLcode result;
if(cf->connected) {
@@ -1502,7 +1510,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- cf_ctx_set_data(cf, data);
+ CF_DATA_SAVE(save, cf, data);
(void)connssl;
DEBUGASSERT(data->conn);
DEBUGASSERT(data->conn == cf->conn);
@@ -1513,10 +1521,10 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
if(result || !*done)
goto out;
- /* TODO: right now we do not fully control when hostname is set,
- * assign it on each connect call. */
- reinit_hostname(cf);
*done = FALSE;
+ result = reinit_hostname(cf);
+ if(result)
+ goto out;
if(blocking) {
result = ssl_connect(cf, data);
@@ -1528,26 +1536,26 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
if(!result && *done) {
cf->connected = TRUE;
- if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
+ connssl->handshake_done = Curl_now();
DEBUGASSERT(connssl->state == ssl_connection_complete);
}
out:
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
+ struct cf_call_data save;
bool result;
- cf_ctx_set_data(cf, (struct Curl_easy *)data);
- if(cf->ctx && Curl_ssl->data_pending(cf, data))
+ CF_DATA_SAVE(save, cf, data);
+ if(Curl_ssl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
@@ -1555,12 +1563,13 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
struct Curl_easy *data, const void *buf, size_t len,
CURLcode *err)
{
+ struct cf_call_data save;
ssize_t nwritten;
+ CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- cf_ctx_set_data(cf, data);
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return nwritten;
}
@@ -1568,12 +1577,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
+ struct cf_call_data save;
ssize_t nread;
+ CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- cf_ctx_set_data(cf, data);
nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return nread;
}
@@ -1581,39 +1591,72 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
+ struct cf_call_data save;
int result;
- cf_ctx_set_data(cf, data);
+ CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->get_select_socks(cf, data, socks);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
-static void ssl_cf_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
{
- if(Curl_ssl->attach_data) {
- cf_ctx_set_data(cf, data);
- Curl_ssl->attach_data(cf, data);
- cf_ctx_set_data(cf, NULL);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct cf_call_data save;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_CONN_REPORT_STATS:
+ if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
+ break;
+ case CF_CTRL_DATA_ATTACH:
+ if(Curl_ssl->attach_data) {
+ CF_DATA_SAVE(save, cf, data);
+ Curl_ssl->attach_data(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ }
+ break;
+ case CF_CTRL_DATA_DETACH:
+ if(Curl_ssl->detach_data) {
+ CF_DATA_SAVE(save, cf, data);
+ Curl_ssl->detach_data(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ }
+ break;
+ default:
+ break;
}
+ return CURLE_OK;
}
-static void ssl_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- if(Curl_ssl->detach_data) {
- cf_ctx_set_data(cf, data);
- Curl_ssl->detach_data(cf, data);
- cf_ctx_set_data(cf, NULL);
- }
+ struct cf_call_data save;
+ bool result;
+ /*
+ * This function tries to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+ CF_DATA_SAVE(save, cf, data);
+ result = Curl_ssl->check_cxn(cf, data) != 0;
+ CF_DATA_RESTORE(cf, save);
+ return result;
}
-static const struct Curl_cftype cft_ssl = {
+struct Curl_cftype Curl_cft_ssl = {
"SSL",
CF_TYPE_SSL,
+ CURL_LOG_DEFAULT,
ssl_cf_destroy,
- Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@@ -1621,15 +1664,17 @@ static const struct Curl_cftype cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_attach_data,
- ssl_cf_detach_data,
+ ssl_cf_cntrl,
+ cf_ssl_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
-static const struct Curl_cftype cft_ssl_proxy = {
+struct Curl_cftype Curl_cft_ssl_proxy = {
"SSL-PROXY",
CF_TYPE_SSL,
+ CURL_LOG_DEFAULT,
ssl_cf_destroy,
- Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@@ -1637,65 +1682,107 @@ static const struct Curl_cftype cft_ssl_proxy = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_attach_data,
- ssl_cf_detach_data,
+ ssl_cf_cntrl,
+ cf_ssl_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
-CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
- struct Curl_cfilter *cf;
+ struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
DEBUGASSERT(data->conn);
- ctx = cf_ctx_new(data);
+
+ ctx = cf_ctx_new(data, Curl_alpn_get_spec(data, conn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- result = Curl_cf_create(&cf, &cft_ssl, ctx);
- if(result)
- goto out;
-
- Curl_conn_cf_add(data, conn, sockindex, cf);
-
- result = CURLE_OK;
+ result = Curl_cf_create(&cf, &Curl_cft_ssl, ctx);
out:
if(result)
cf_ctx_free(ctx);
+ *pcf = result? NULL : cf;
return result;
}
-#ifndef CURL_DISABLE_PROXY
-CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
{
struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_create(&cf, data, conn);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
+
+CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_create(&cf, data, cf_at->conn);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
+#ifndef CURL_DISABLE_PROXY
+static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
- ctx = cf_ctx_new(data);
+ ctx = cf_ctx_new(data, Curl_alpn_get_proxy_spec(data, conn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
+ result = Curl_cf_create(&cf, &Curl_cft_ssl_proxy, ctx);
- result = Curl_cf_create(&cf, &cft_ssl_proxy, ctx);
+out:
if(result)
- goto out;
+ cf_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
+}
- Curl_conn_cf_add(data, conn, sockindex, cf);
+CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
- result = CURLE_OK;
+ result = cf_ssl_proxy_create(&cf, data, conn);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
-out:
- if(result)
- cf_ctx_free(ctx);
+CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_proxy_create(&cf, data, cf_at->conn);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
@@ -1717,9 +1804,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
/* get first filter in chain, if any is present */
cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
if(cf) {
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+ CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->get_internals(cf->ctx, info);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
}
}
return result;
@@ -1733,7 +1821,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
(void)data;
for(; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl) {
+ if(cf->cft == &Curl_cft_ssl) {
if(Curl_ssl->shut_down(cf, data))
result = CURLE_SSL_SHUTDOWN_FAILED;
Curl_conn_cf_discard(cf, data);
@@ -1749,7 +1837,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
struct Curl_cfilter *cf, *lowest_ssl_cf = NULL;
for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy) {
+ if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) {
lowest_ssl_cf = cf;
if(cf->connected || (cf->next && cf->next->connected)) {
/* connected or about to start */
@@ -1762,7 +1850,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
{
- return (cf->cft == &cft_ssl_proxy);
+ return (cf->cft == &Curl_cft_ssl_proxy);
}
struct ssl_config_data *
@@ -1814,10 +1902,142 @@ Curl_ssl_get_primary_config(struct Curl_easy *data,
struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy)
+ if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy)
return cf;
}
return NULL;
}
+static const struct alpn_spec ALPN_SPEC_H10 = {
+ { ALPN_HTTP_1_0 }, 1
+};
+static const struct alpn_spec ALPN_SPEC_H11 = {
+ { ALPN_HTTP_1_1 }, 1
+};
+#ifdef USE_HTTP2
+static const struct alpn_spec ALPN_SPEC_H2_H11 = {
+ { ALPN_H2, ALPN_HTTP_1_1 }, 2
+};
+#endif
+
+const struct alpn_spec *
+Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(!conn->bits.tls_enable_alpn)
+ return NULL;
+ if(data->state.httpwant == CURL_HTTP_VERSION_1_0)
+ return &ALPN_SPEC_H10;
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2)
+ return &ALPN_SPEC_H2_H11;
+#endif
+ return &ALPN_SPEC_H11;
+}
+
+const struct alpn_spec *
+Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(!conn->bits.tls_enable_alpn)
+ return NULL;
+ if(data->state.httpwant == CURL_HTTP_VERSION_1_0)
+ return &ALPN_SPEC_H10;
+ return &ALPN_SPEC_H11;
+}
+
+CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec)
+{
+ size_t i, len;
+ int off = 0;
+ unsigned char blen;
+
+ memset(buf, 0, sizeof(*buf));
+ for(i = 0; spec && i < spec->count; ++i) {
+ len = strlen(spec->entries[i]);
+ if(len >= ALPN_NAME_MAX)
+ return CURLE_FAILED_INIT;
+ blen = (unsigned char)len;
+ if(off + blen + 1 >= (int)sizeof(buf->data))
+ return CURLE_FAILED_INIT;
+ buf->data[off++] = blen;
+ memcpy(buf->data + off, spec->entries[i], blen);
+ off += blen;
+ }
+ buf->len = off;
+ return CURLE_OK;
+}
+
+CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec)
+{
+ size_t i, len;
+ size_t off = 0;
+
+ memset(buf, 0, sizeof(*buf));
+ for(i = 0; spec && i < spec->count; ++i) {
+ len = strlen(spec->entries[i]);
+ if(len >= ALPN_NAME_MAX)
+ return CURLE_FAILED_INIT;
+ if(off + len + 2 >= (int)sizeof(buf->data))
+ return CURLE_FAILED_INIT;
+ if(off)
+ buf->data[off++] = ',';
+ memcpy(buf->data + off, spec->entries[i], len);
+ off += len;
+ }
+ buf->data[off] = '\0';
+ buf->len = (int)off;
+ return CURLE_OK;
+}
+
+CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const unsigned char *proto,
+ size_t proto_len)
+{
+ int can_multi = 0;
+
+ if(proto && proto_len) {
+ if(proto_len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ else if(proto_len == ALPN_HTTP_1_0_LENGTH &&
+ !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_0;
+ }
+#ifdef USE_HTTP2
+ else if(proto_len == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ can_multi = 1;
+ }
+#endif
+#ifdef USE_HTTP3
+ else if(proto_len == ALPN_H3_LENGTH &&
+ !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ can_multi = 1;
+ }
+#endif
+ else {
+ cf->conn->alpn = CURL_HTTP_VERSION_NONE;
+ failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
+ /* TODO: do we want to fail this? Previous code just ignored it and
+ * some vtls backends even ignore the return code of this function. */
+ /* return CURLE_NOT_BUILT_IN; */
+ goto out;
+ }
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto);
+ }
+ else {
+ cf->conn->alpn = CURL_HTTP_VERSION_NONE;
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+
+out:
+ Curl_multiuse_state(data, can_multi? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ return CURLE_OK;
+}
+
#endif /* USE_SSL */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 5ad64fcf60..0d9e74a699 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,6 @@
struct connectdata;
struct ssl_config_data;
-struct ssl_connect_data;
struct ssl_primary_config;
struct Curl_ssl_session;
@@ -53,6 +52,7 @@ struct Curl_ssl_session;
/* Curl_multi SSL backend-specific data; declared differently by each SSL
backend */
struct multi_ssl_backend_data;
+struct Curl_cfilter;
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@@ -68,8 +68,53 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
#define ALPN_HTTP_1_1_LENGTH 8
#define ALPN_HTTP_1_1 "http/1.1"
+#define ALPN_HTTP_1_0_LENGTH 8
+#define ALPN_HTTP_1_0 "http/1.0"
#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))
+
+struct alpn_spec {
+ const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
+ size_t count; /* number of entries */
+};
+
+struct alpn_proto_buf {
+ unsigned char data[ALPN_PROTO_BUF_MAX];
+ int len;
+};
+
+CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec);
+CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec);
+
+CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const unsigned char *proto,
+ size_t proto_len);
+
+/**
+ * Get the ALPN specification to use for talking to remote host.
+ * May return NULL if ALPN is disabled on the connection.
+ */
+const struct alpn_spec *
+Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn);
+
+/**
+ * Get the ALPN specification to use for talking to the proxy.
+ * May return NULL if ALPN is disabled on the connection.
+ */
+const struct alpn_spec *
+Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn);
char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen);
@@ -95,7 +140,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
/* init the SSL session ID cache */
CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
-int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn);
/* Certificate information list handling. */
@@ -156,6 +200,9 @@ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
int sockindex);
@@ -163,6 +210,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
#endif /* !CURL_DISABLE_PROXY */
/**
@@ -208,6 +257,9 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option);
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n);
+extern struct Curl_cftype Curl_cft_ssl;
+extern struct Curl_cftype Curl_cft_ssl_proxy;
+
#else /* if not USE_SSL */
/* When SSL support is not present, just define away these function calls */
@@ -218,7 +270,6 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
#define Curl_ssl_initsessions(x,y) CURLE_OK
-#define Curl_ssl_check_cxn(d,x) 0
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h
index 6710a2b559..a20ca7db7f 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_int.h
+++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,18 +33,20 @@
struct ssl_connect_data {
ssl_connection_state state;
ssl_connect_state connecting_state;
- const char *hostname; /* hostnaem for verification */
- const char *dispname; /* display version of hostname */
+ char *hostname; /* hostname for verification */
+ char *dispname; /* display version of hostname */
int port; /* remote port at origin */
+ const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
struct ssl_backend_data *backend; /* vtls backend specific props */
- struct Curl_easy *call_data; /* data handle used in current call,
- * same as parameter passed, but available
- * here for backend internal callbacks
- * that need it. NULLed after at the
- * end of each vtls filter invcocation. */
+ struct cf_call_data call_data; /* data handle used in current call */
+ struct curltime handshake_done; /* time when handshake finished */
};
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct ssl_connect_data *)(cf)->ctx)->call_data
+
+
/* Definitions for SSL Implementations */
struct Curl_ssl {
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index 7cc4774e83..2e57899324 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -218,29 +218,9 @@ static const struct group_name_map gnm[] = {
{ WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
{ WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
{ WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
- { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" },
- { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" },
- { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" },
- { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" },
- { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" },
- { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" },
- { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" },
- { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" },
- { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" },
- { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" },
- { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" },
- { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" },
- { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" },
- { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" },
- { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" },
- { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" },
- { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" },
{ WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
{ WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
{ WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
- { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" },
- { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" },
- { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" },
{ 0, NULL }
};
#endif
@@ -299,8 +279,7 @@ static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result = CURLE_OK;
@@ -315,8 +294,7 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_OK;
@@ -633,29 +611,18 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
#ifdef HAVE_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- char protocols[128];
- *protocols = '\0';
-
- /* wolfSSL's ALPN protocol name list format is a comma separated string of
- protocols in descending order of preference, eg: "h2,http/1.1" */
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
- strcpy(protocols + strlen(protocols), ALPN_H2 ",");
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- }
-#endif
-
- strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ CURLcode result;
- if(wolfSSL_UseALPN(backend->handle, protocols,
- (unsigned)strlen(protocols),
+ result = Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ if(result ||
+ wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len,
WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) {
failf(data, "SSL: failed setting ALPN protocols");
return CURLE_SSL_CONNECT_ERROR;
}
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif /* HAVE_ALPN */
@@ -707,7 +674,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#else /* USE_BIO_CHAIN */
/* pass the raw socket into the SSL layer */
- if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) {
+ if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) {
failf(data, "SSL: SSL_set_fd failed");
return CURLE_SSL_CONNECT_ERROR;
}
@@ -883,25 +850,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
if(rc == SSL_SUCCESS) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol);
-
- if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
-#ifdef USE_HTTP2
- else if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
- protocol_len == ALPN_H2_LENGTH &&
- !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH))
- cf->conn->alpn = CURL_HTTP_VERSION_2;
-#endif
- else
- infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
- protocol);
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol,
+ protocol_len);
}
else if(rc == SSL_ALPN_NOT_FOUND)
- infof(data, VTLS_INFOF_NO_ALPN);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
else {
failf(data, "ALPN, failure getting protocol, error %d", rc);
return CURLE_SSL_CONNECT_ERROR;
@@ -1166,7 +1119,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h
index b2e7c3fde2..a5ed848099 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.h
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c
index 4c1c9a8b79..39e4fb33bc 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.c
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,6 +48,7 @@
#include "curl_ctype.h"
#include "hostcheck.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_int.h"
#include "sendf.h"
#include "inet_pton.h"
#include "curl_base64.h"
@@ -1313,7 +1314,8 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
/* Get the server IP address. */
#ifdef ENABLE_IPV6
- if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
+ if(cf->conn->bits.ipv6_ip &&
+ Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
addrlen = sizeof(struct in6_addr);
else
#endif
@@ -1348,19 +1350,18 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
break;
switch(name.tag) {
case 2: /* DNS name. */
- matched = 0;
len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
name.beg, name.end);
- if(len > 0) {
- if(size_t)len == strlen(dnsname)
- matched = Curl_cert_hostcheck(dnsname, (size_t)len,
- connssl->hostname, hostlen);
- free(dnsname);
- }
+ if(len > 0 && (size_t)len == strlen(dnsname))
+ matched = Curl_cert_hostcheck(dnsname, (size_t)len,
+ connssl->hostname, hostlen);
+ else
+ matched = 0;
+ free(dnsname);
break;
case 7: /* IP address. */
- matched = (name.end - name.beg) == addrlen &&
+ matched = (size_t)(name.end - name.beg) == addrlen &&
!memcmp(&addr, name.beg, addrlen);
break;
}
@@ -1406,8 +1407,10 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
failf(data, "SSL: unable to obtain common name from peer certificate");
else {
len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
- if(len < 0)
+ if(len < 0) {
+ free(dnsname);
return CURLE_OUT_OF_MEMORY;
+ }
if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */
failf(data, "SSL: illegal cert name field");
else if(Curl_cert_hostcheck((const char *) dnsname,
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h
index eb8e9597cb..5496de40e4 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.h
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index b00d7a5a26..10c91fb28f 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h
index 4367099d92..99b243379e 100644
--- a/Utilities/cmcurl/lib/warnless.h
+++ b/Utilities/cmcurl/lib/warnless.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/wildcard.c b/Utilities/cmcurl/lib/wildcard.c
index a3e24b6784..3b81c7a869 100644
--- a/Utilities/cmcurl/lib/wildcard.c
+++ b/Utilities/cmcurl/lib/wildcard.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/wildcard.h b/Utilities/cmcurl/lib/wildcard.h
index 21e933b9a4..9dccab01ab 100644
--- a/Utilities/cmcurl/lib/wildcard.h
+++ b/Utilities/cmcurl/lib/wildcard.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
index c1b2622a71..0fc5e56aee 100644
--- a/Utilities/cmcurl/lib/ws.c
+++ b/Utilities/cmcurl/lib/ws.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -115,12 +115,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
return result;
}
-CURLcode Curl_ws_accept(struct Curl_easy *data)
+/*
+ * 'nread' is number of bytes of websocket data already in the buffer at
+ * 'mem'.
+ */
+CURLcode Curl_ws_accept(struct Curl_easy *data,
+ const char *mem, size_t nread)
{
struct SingleRequest *k = &data->req;
struct HTTP *ws = data->req.p.http;
struct connectdata *conn = data->conn;
struct websocket *wsp = &data->req.p.http->ws;
+ struct ws_conn *wsc = &conn->proto.ws;
CURLcode result;
/* Verify the Sec-WebSocket-Accept response.
@@ -149,13 +155,21 @@ CURLcode Curl_ws_accept(struct Curl_easy *data)
infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
+ Curl_dyn_init(&wsc->early, data->set.buffer_size);
+ if(nread) {
+ result = Curl_dyn_addn(&wsc->early, mem, nread);
+ if(result)
+ return result;
+ infof(data, "%zu bytes websocket payload", nread);
+ wsp->stillb = Curl_dyn_ptr(&wsc->early);
+ wsp->stillblen = Curl_dyn_len(&wsc->early);
+ }
k->upgr101 = UPGR101_RECEIVED;
if(data->set.connect_only)
/* switch off non-blocking sockets */
(void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE);
- wsp->oleft = 0;
return result;
}
@@ -183,12 +197,12 @@ static void ws_decode_shift(struct Curl_easy *data, size_t spent)
/* ws_decode() decodes a binary frame into structured WebSocket data,
- wpkt - the incoming raw data. If NULL, work on the already buffered data.
- ilen - the size of the provided data, perhaps too little, perhaps too much
- out - stored pointed to extracted data
+ data - the transfer
+ inbuf - incoming raw data. If NULL, work on the already buffered data.
+ inlen - size of the provided data, perhaps too little, perhaps too much
+ headlen - stored length of the frame header
olen - stored length of the extracted data
oleft - number of unread bytes pending to that belongs to this frame
- more - if there is more data in there
flags - stored bitmask about the frame
Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
@@ -246,6 +260,9 @@ static CURLcode ws_decode(struct Curl_easy *data,
infof(data, "WS: received OPCODE PONG");
*flags |= CURLWS_PONG;
break;
+ default:
+ failf(data, "WS: unknown opcode: %x", opcode);
+ return CURLE_RECV_ERROR;
}
if(inbuf[1] & WSBIT_MASK) {
@@ -296,7 +313,7 @@ static CURLcode ws_decode(struct Curl_easy *data,
*oleft = 0; /* bytes yet to come (for this frame) */
}
- infof(data, "WS: received %zu bytes payload (%zu left, buflen was %zu)",
+ infof(data, "WS: received %Ou bytes payload (%Ou left, buflen was %zu)",
payloadsize, *oleft, inlen);
return CURLE_OK;
}
@@ -339,9 +356,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
result = ws_decode(data, wsbuf, buflen,
&headlen, &write_len, &fb_left, &recvflags);
- consumed += headlen;
- wsbuf += headlen;
- buflen -= headlen;
if(result == CURLE_AGAIN)
/* insufficient amount of data, keep it for later.
* we pretend to have written all since we have a copy */
@@ -350,6 +364,10 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
infof(data, "WS: decode error %d", (int)result);
return nitems - 1;
}
+ consumed += headlen;
+ wsbuf += headlen;
+ buflen -= headlen;
+
/* New frame. store details about the frame to be reachable with
curl_ws_meta() from within the write callback */
ws->ws.frame.age = 0;
@@ -392,7 +410,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
return nitems;
}
-
CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
size_t buflen, size_t *nread,
struct curl_ws_frame **metap)
@@ -409,7 +426,7 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
return result;
while(!done) {
- size_t write_len;
+ size_t datalen;
unsigned int recvflags;
if(!wsp->stillblen) {
@@ -419,26 +436,32 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
data->set.buffer_size, &n);
if(result)
return result;
- if(!n)
+ if(!n) {
/* connection closed */
+ infof(data, "connection expectedly closed?");
return CURLE_GOT_NOTHING;
+ }
wsp->stillb = data->state.buffer;
wsp->stillblen = n;
}
- infof(data, "WS: got %u websocket bytes to decode",
- (int)wsp->stillblen);
+ infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen);
if(!wsp->frame.bytesleft) {
size_t headlen;
curl_off_t oleft;
/* detect new frame */
result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
- &headlen, &write_len, &oleft, &recvflags);
+ &headlen, &datalen, &oleft, &recvflags);
if(result == CURLE_AGAIN)
/* a packet fragment only */
break;
else if(result)
return result;
+ if(datalen > buflen) {
+ size_t diff = datalen - buflen;
+ datalen = buflen;
+ oleft += diff;
+ }
wsp->stillb += headlen;
wsp->stillblen -= headlen;
wsp->frame.offset = 0;
@@ -447,40 +470,45 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
}
else {
/* existing frame, remaining payload handling */
- write_len = wsp->frame.bytesleft;
- if(write_len > wsp->stillblen)
- write_len = wsp->stillblen;
+ datalen = wsp->frame.bytesleft;
+ if(datalen > wsp->stillblen)
+ datalen = wsp->stillblen;
+ if(datalen > buflen)
+ datalen = buflen;
+
+ wsp->frame.offset += wsp->frame.len;
+ wsp->frame.bytesleft -= datalen;
}
+ wsp->frame.len = datalen;
/* auto-respond to PINGs */
if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
- infof(data, "WS: auto-respond to PING with a PONG");
+ size_t nsent = 0;
+ infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload",
+ datalen);
/* send back the exact same content as a PONG */
- result = curl_ws_send(data, wsp->stillb, write_len,
- &write_len, 0, CURLWS_PONG);
+ result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0,
+ CURLWS_PONG);
if(result)
return result;
+ infof(data, "WS: bytesleft %zu datalen %zu",
+ wsp->frame.bytesleft, datalen);
+ /* we handled the data part of the PING, advance over that */
+ wsp->stillb += nsent;
+ wsp->stillblen -= nsent;
}
- else if(write_len || !wsp->frame.bytesleft) {
- if(write_len > buflen)
- write_len = buflen;
+ else if(datalen) {
/* copy the payload to the user buffer */
- memcpy(buffer, wsp->stillb, write_len);
- *nread = write_len;
+ memcpy(buffer, wsp->stillb, datalen);
+ *nread = datalen;
done = TRUE;
- }
- if(write_len) {
- /* update buffer and frame info */
- wsp->frame.offset += write_len;
- DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len);
- if(wsp->frame.bytesleft)
- wsp->frame.bytesleft -= write_len;
- DEBUGASSERT(write_len <= wsp->stillblen);
- wsp->stillblen -= write_len;
+
+ wsp->stillblen -= datalen;
if(wsp->stillblen)
- wsp->stillb += write_len;
- else
+ wsp->stillb += datalen;
+ else {
wsp->stillb = NULL;
+ }
}
}
*metap = &wsp->frame;
@@ -555,17 +583,27 @@ static size_t ws_packethead(struct Curl_easy *data,
}
if(!(flags & CURLWS_CONT)) {
- /* if not marked as continuing, assume this is the final fragment */
- firstbyte |= WSBIT_FIN | opcode;
+ if(!ws->ws.contfragment)
+ /* not marked as continuing, this is the final fragment */
+ firstbyte |= WSBIT_FIN | opcode;
+ else
+ /* marked as continuing, this is the final fragment; set CONT
+ opcode and FIN bit */
+ firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
+
ws->ws.contfragment = FALSE;
+ infof(data, "WS: set FIN");
}
else if(ws->ws.contfragment) {
/* the previous fragment was not a final one and this isn't either, keep a
CONT opcode and no FIN bit */
firstbyte |= WSBIT_OPCODE_CONT;
+ infof(data, "WS: keep CONT, no FIN");
}
else {
+ firstbyte = opcode;
ws->ws.contfragment = TRUE;
+ infof(data, "WS: set CONT, no FIN");
}
out[0] = firstbyte;
if(len > 65535) {
@@ -686,8 +724,14 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
headlen + buflen, written);
- *sent = written;
+ if(!result) {
+ /* the *sent number only counts "payload", excluding the header */
+ if((size_t)written > headlen)
+ *sent = written - headlen;
+ else
+ *sent = 0;
+ }
return result;
}
@@ -698,6 +742,20 @@ void Curl_ws_done(struct Curl_easy *data)
Curl_dyn_free(&wsp->buf);
}
+CURLcode Curl_ws_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct ws_conn *wsc = &conn->proto.ws;
+ (void)data;
+ (void)dead_connection;
+ Curl_dyn_free(&wsc->early);
+
+ /* make sure this is non-blocking to avoid getting stuck in shutdown */
+ (void)curlx_nonblock(conn->sock[FIRSTSOCKET], TRUE);
+ return CURLE_OK;
+}
+
CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
{
/* we only return something for websocket, called from within the callback
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h
index 2f3ed2d1e6..176dda470b 100644
--- a/Utilities/cmcurl/lib/ws.h
+++ b/Utilities/cmcurl/lib/ws.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -46,24 +46,28 @@ struct websocket {
size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete
websocket frame uses */
struct curl_ws_frame frame; /* the struct used for frame state */
- curl_off_t oleft; /* outstanding number of payload bytes left from the
- server */
size_t stillblen; /* number of bytes left in the buffer to deliver in
the next curl_ws_recv() call */
- char *stillb; /* the stillblen pending bytes are here */
+ const char *stillb; /* the stillblen pending bytes are here */
curl_off_t sleft; /* outstanding number of payload bytes left to send */
unsigned int xori; /* xor index */
};
-CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
-CURLcode Curl_ws_accept(struct Curl_easy *data);
+struct ws_conn {
+ struct dynbuf early; /* data already read when switching to ws */
+};
+CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
+CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
void Curl_ws_done(struct Curl_easy *data);
-
+CURLcode Curl_ws_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
#else
#define Curl_ws_request(x,y) CURLE_OK
#define Curl_ws_done(x) Curl_nop_stmt
+#define Curl_ws_free(x) Curl_nop_stmt
#endif
#endif /* HEADER_CURL_WS_H */