lib: connection filters (cfilter) addition to curl:
—
$ git checkout -f dafdb20a26d0c890e83dea61a104b75408481ebd
Previous HEAD position was bf12c2bed6 lib: remove bad set.opt_no_body assignments
HEAD is now at dafdb20a26 lib: connection filters (cfilter) addition to curl:
$ git clean -fdx
Removing Makefile
Removing Makefile.in
Removing aclocal.m4
Removing autom4te.cache/
Removing compile
Removing config.guess
Removing config.log
Removing config.status
Removing config.sub
Removing configure
Removing curl-config
Removing depcomp
Removing docs/Makefile
Removing docs/Makefile.in
Removing docs/cmdline-opts/Makefile
Removing docs/cmdline-opts/Makefile.in
Removing docs/curl.1
Removing docs/examples/.deps/
Removing docs/examples/Makefile
Removing docs/examples/Makefile.in
Removing docs/libcurl/Makefile
Removing docs/libcurl/Makefile.in
Removing docs/libcurl/opts/Makefile
Removing docs/libcurl/opts/Makefile.in
Removing include/Makefile
Removing include/Makefile.in
Removing include/curl/Makefile
Removing include/curl/Makefile.in
Removing install-sh
Removing lib/.deps/
Removing lib/.dirstamp
Removing lib/.libs/
Removing lib/Makefile
Removing lib/Makefile.in
Removing lib/curl_config.h
Removing lib/curl_config.h.in
Removing lib/curl_multibyte.o
Removing lib/dynbuf.o
Removing lib/libcurl.la
Removing lib/libcurl.plist
Removing lib/libcurl.vers
Removing lib/libcurl_la-altsvc.lo
Removing lib/libcurl_la-altsvc.o
Removing lib/libcurl_la-amigaos.lo
Removing lib/libcurl_la-amigaos.o
Removing lib/libcurl_la-asyn-ares.lo
Removing lib/libcurl_la-asyn-ares.o
Removing lib/libcurl_la-asyn-thread.lo
Removing lib/libcurl_la-asyn-thread.o
Removing lib/libcurl_la-base64.lo
Removing lib/libcurl_la-base64.o
Removing lib/libcurl_la-bufref.lo
Removing lib/libcurl_la-bufref.o
Removing lib/libcurl_la-c-hyper.lo
Removing lib/libcurl_la-c-hyper.o
Removing lib/libcurl_la-cfilters.lo
Removing lib/libcurl_la-cfilters.o
Removing lib/libcurl_la-conncache.lo
Removing lib/libcurl_la-conncache.o
Removing lib/libcurl_la-connect.lo
Removing lib/libcurl_la-connect.o
Removing lib/libcurl_la-content_encoding.lo
Removing lib/libcurl_la-content_encoding.o
Removing lib/libcurl_la-cookie.lo
Removing lib/libcurl_la-cookie.o
Removing lib/libcurl_la-curl_addrinfo.lo
Removing lib/libcurl_la-curl_addrinfo.o
Removing lib/libcurl_la-curl_des.lo
Removing lib/libcurl_la-curl_des.o
Removing lib/libcurl_la-curl_endian.lo
Removing lib/libcurl_la-curl_endian.o
Removing lib/libcurl_la-curl_fnmatch.lo
Removing lib/libcurl_la-curl_fnmatch.o
Removing lib/libcurl_la-curl_get_line.lo
Removing lib/libcurl_la-curl_get_line.o
Removing lib/libcurl_la-curl_gethostname.lo
Removing lib/libcurl_la-curl_gethostname.o
Removing lib/libcurl_la-curl_gssapi.lo
Removing lib/libcurl_la-curl_gssapi.o
Removing lib/libcurl_la-curl_memrchr.lo
Removing lib/libcurl_la-curl_memrchr.o
Removing lib/libcurl_la-curl_multibyte.lo
Removing lib/libcurl_la-curl_multibyte.o
Removing lib/libcurl_la-curl_ntlm_core.lo
Removing lib/libcurl_la-curl_ntlm_core.o
Removing lib/libcurl_la-curl_ntlm_wb.lo
Removing lib/libcurl_la-curl_ntlm_wb.o
Removing lib/libcurl_la-curl_path.lo
Removing lib/libcurl_la-curl_path.o
Removing lib/libcurl_la-curl_range.lo
Removing lib/libcurl_la-curl_range.o
Removing lib/libcurl_la-curl_rtmp.lo
Removing lib/libcurl_la-curl_rtmp.o
Removing lib/libcurl_la-curl_sasl.lo
Removing lib/libcurl_la-curl_sasl.o
Removing lib/libcurl_la-curl_sspi.lo
Removing lib/libcurl_la-curl_sspi.o
Removing lib/libcurl_la-curl_threads.lo
Removing lib/libcurl_la-curl_threads.o
Removing lib/libcurl_la-dict.lo
Removing lib/libcurl_la-dict.o
Removing lib/libcurl_la-doh.lo
Removing lib/libcurl_la-doh.o
Removing lib/libcurl_la-dynbuf.lo
Removing lib/libcurl_la-dynbuf.o
Removing lib/libcurl_la-easy.lo
Removing lib/libcurl_la-easy.o
Removing lib/libcurl_la-easygetopt.lo
Removing lib/libcurl_la-easygetopt.o
Removing lib/libcurl_la-easyoptions.lo
Removing lib/libcurl_la-easyoptions.o
Removing lib/libcurl_la-escape.lo
Removing lib/libcurl_la-escape.o
Removing lib/libcurl_la-file.lo
Removing lib/libcurl_la-file.o
Removing lib/libcurl_la-fileinfo.lo
Removing lib/libcurl_la-fileinfo.o
Removing lib/libcurl_la-fopen.lo
Removing lib/libcurl_la-fopen.o
Removing lib/libcurl_la-formdata.lo
Removing lib/libcurl_la-formdata.o
Removing lib/libcurl_la-ftp.lo
Removing lib/libcurl_la-ftp.o
Removing lib/libcurl_la-ftplistparser.lo
Removing lib/libcurl_la-ftplistparser.o
Removing lib/libcurl_la-getenv.lo
Removing lib/libcurl_la-getenv.o
Removing lib/libcurl_la-getinfo.lo
Removing lib/libcurl_la-getinfo.o
Removing lib/libcurl_la-gopher.lo
Removing lib/libcurl_la-gopher.o
Removing lib/libcurl_la-h2h3.lo
Removing lib/libcurl_la-h2h3.o
Removing lib/libcurl_la-hash.lo
Removing lib/libcurl_la-hash.o
Removing lib/libcurl_la-headers.lo
Removing lib/libcurl_la-headers.o
Removing lib/libcurl_la-hmac.lo
Removing lib/libcurl_la-hmac.o
Removing lib/libcurl_la-hostasyn.lo
Removing lib/libcurl_la-hostasyn.o
Removing lib/libcurl_la-hostip.lo
Removing lib/libcurl_la-hostip.o
Removing lib/libcurl_la-hostip4.lo
Removing lib/libcurl_la-hostip4.o
Removing lib/libcurl_la-hostip6.lo
Removing lib/libcurl_la-hostip6.o
Removing lib/libcurl_la-hostsyn.lo
Removing lib/libcurl_la-hostsyn.o
Removing lib/libcurl_la-hsts.lo
Removing lib/libcurl_la-hsts.o
Removing lib/libcurl_la-http.lo
Removing lib/libcurl_la-http.o
Removing lib/libcurl_la-http2.lo
Removing lib/libcurl_la-http2.o
Removing lib/libcurl_la-http_aws_sigv4.lo
Removing lib/libcurl_la-http_aws_sigv4.o
Removing lib/libcurl_la-http_chunks.lo
Removing lib/libcurl_la-http_chunks.o
Removing lib/libcurl_la-http_digest.lo
Removing lib/libcurl_la-http_digest.o
Removing lib/libcurl_la-http_negotiate.lo
Removing lib/libcurl_la-http_negotiate.o
Removing lib/libcurl_la-http_ntlm.lo
Removing lib/libcurl_la-http_ntlm.o
Removing lib/libcurl_la-http_proxy.lo
Removing lib/libcurl_la-http_proxy.o
Removing lib/libcurl_la-idn_win32.lo
Removing lib/libcurl_la-idn_win32.o
Removing lib/libcurl_la-if2ip.lo
Removing lib/libcurl_la-if2ip.o
Removing lib/libcurl_la-imap.lo
Removing lib/libcurl_la-imap.o
Removing lib/libcurl_la-inet_ntop.lo
Removing lib/libcurl_la-inet_ntop.o
Removing lib/libcurl_la-inet_pton.lo
Removing lib/libcurl_la-inet_pton.o
Removing lib/libcurl_la-krb5.lo
Removing lib/libcurl_la-krb5.o
Removing lib/libcurl_la-ldap.lo
Removing lib/libcurl_la-ldap.o
Removing lib/libcurl_la-llist.lo
Removing lib/libcurl_la-llist.o
Removing lib/libcurl_la-md4.lo
Removing lib/libcurl_la-md4.o
Removing lib/libcurl_la-md5.lo
Removing lib/libcurl_la-md5.o
Removing lib/libcurl_la-memdebug.lo
Removing lib/libcurl_la-memdebug.o
Removing lib/libcurl_la-mime.lo
Removing lib/libcurl_la-mime.o
Removing lib/libcurl_la-mprintf.lo
Removing lib/libcurl_la-mprintf.o
Removing lib/libcurl_la-mqtt.lo
Removing lib/libcurl_la-mqtt.o
Removing lib/libcurl_la-multi.lo
Removing lib/libcurl_la-multi.o
Removing lib/libcurl_la-netrc.lo
Removing lib/libcurl_la-netrc.o
Removing lib/libcurl_la-nonblock.lo
Removing lib/libcurl_la-nonblock.o
Removing lib/libcurl_la-noproxy.lo
Removing lib/libcurl_la-noproxy.o
Removing lib/libcurl_la-openldap.lo
Removing lib/libcurl_la-openldap.o
Removing lib/libcurl_la-parsedate.lo
Removing lib/libcurl_la-parsedate.o
Removing lib/libcurl_la-pingpong.lo
Removing lib/libcurl_la-pingpong.o
Removing lib/libcurl_la-pop3.lo
Removing lib/libcurl_la-pop3.o
Removing lib/libcurl_la-progress.lo
Removing lib/libcurl_la-progress.o
Removing lib/libcurl_la-psl.lo
Removing lib/libcurl_la-psl.o
Removing lib/libcurl_la-rand.lo
Removing lib/libcurl_la-rand.o
Removing lib/libcurl_la-rename.lo
Removing lib/libcurl_la-rename.o
Removing lib/libcurl_la-rtsp.lo
Removing lib/libcurl_la-rtsp.o
Removing lib/libcurl_la-select.lo
Removing lib/libcurl_la-select.o
Removing lib/libcurl_la-sendf.lo
Removing lib/libcurl_la-sendf.o
Removing lib/libcurl_la-setopt.lo
Removing lib/libcurl_la-setopt.o
Removing lib/libcurl_la-sha256.lo
Removing lib/libcurl_la-sha256.o
Removing lib/libcurl_la-share.lo
Removing lib/libcurl_la-share.o
Removing lib/libcurl_la-slist.lo
Removing lib/libcurl_la-slist.o
Removing lib/libcurl_la-smb.lo
Removing lib/libcurl_la-smb.o
Removing lib/libcurl_la-smtp.lo
Removing lib/libcurl_la-smtp.o
Removing lib/libcurl_la-socketpair.lo
Removing lib/libcurl_la-socketpair.o
Removing lib/libcurl_la-socks.lo
Removing lib/libcurl_la-socks.o
Removing lib/libcurl_la-socks_gssapi.lo
Removing lib/libcurl_la-socks_gssapi.o
Removing lib/libcurl_la-socks_sspi.lo
Removing lib/libcurl_la-socks_sspi.o
Removing lib/libcurl_la-speedcheck.lo
Removing lib/libcurl_la-speedcheck.o
Removing lib/libcurl_la-splay.lo
Removing lib/libcurl_la-splay.o
Removing lib/libcurl_la-strcase.lo
Removing lib/libcurl_la-strcase.o
Removing lib/libcurl_la-strdup.lo
Removing lib/libcurl_la-strdup.o
Removing lib/libcurl_la-strerror.lo
Removing lib/libcurl_la-strerror.o
Removing lib/libcurl_la-strtok.lo
Removing lib/libcurl_la-strtok.o
Removing lib/libcurl_la-strtoofft.lo
Removing lib/libcurl_la-strtoofft.o
Removing lib/libcurl_la-system_win32.lo
Removing lib/libcurl_la-system_win32.o
Removing lib/libcurl_la-telnet.lo
Removing lib/libcurl_la-telnet.o
Removing lib/libcurl_la-tftp.lo
Removing lib/libcurl_la-tftp.o
Removing lib/libcurl_la-timediff.lo
Removing lib/libcurl_la-timediff.o
Removing lib/libcurl_la-timeval.lo
Removing lib/libcurl_la-timeval.o
Removing lib/libcurl_la-transfer.lo
Removing lib/libcurl_la-transfer.o
Removing lib/libcurl_la-url.lo
Removing lib/libcurl_la-url.o
Removing lib/libcurl_la-urlapi.lo
Removing lib/libcurl_la-urlapi.o
Removing lib/libcurl_la-version.lo
Removing lib/libcurl_la-version.o
Removing lib/libcurl_la-version_win32.lo
Removing lib/libcurl_la-version_win32.o
Removing lib/libcurl_la-warnless.lo
Removing lib/libcurl_la-warnless.o
Removing lib/libcurl_la-wildcard.lo
Removing lib/libcurl_la-wildcard.o
Removing lib/libcurl_la-ws.lo
Removing lib/libcurl_la-ws.o
Removing lib/nonblock.o
Removing lib/stamp-h1
Removing lib/strtoofft.o
Removing lib/timediff.o
Removing lib/vauth/.deps/
Removing lib/vauth/.dirstamp
Removing lib/vauth/libcurl_la-cleartext.lo
Removing lib/vauth/libcurl_la-cleartext.o
Removing lib/vauth/libcurl_la-cram.lo
Removing lib/vauth/libcurl_la-cram.o
Removing lib/vauth/libcurl_la-digest.lo
Removing lib/vauth/libcurl_la-digest.o
Removing lib/vauth/libcurl_la-digest_sspi.lo
Removing lib/vauth/libcurl_la-digest_sspi.o
Removing lib/vauth/libcurl_la-gsasl.lo
Removing lib/vauth/libcurl_la-gsasl.o
Removing lib/vauth/libcurl_la-krb5_gssapi.lo
Removing lib/vauth/libcurl_la-krb5_gssapi.o
Removing lib/vauth/libcurl_la-krb5_sspi.lo
Removing lib/vauth/libcurl_la-krb5_sspi.o
Removing lib/vauth/libcurl_la-ntlm.lo
Removing lib/vauth/libcurl_la-ntlm.o
Removing lib/vauth/libcurl_la-ntlm_sspi.lo
Removing lib/vauth/libcurl_la-ntlm_sspi.o
Removing lib/vauth/libcurl_la-oauth2.lo
Removing lib/vauth/libcurl_la-oauth2.o
Removing lib/vauth/libcurl_la-spnego_gssapi.lo
Removing lib/vauth/libcurl_la-spnego_gssapi.o
Removing lib/vauth/libcurl_la-spnego_sspi.lo
Removing lib/vauth/libcurl_la-spnego_sspi.o
Removing lib/vauth/libcurl_la-vauth.lo
Removing lib/vauth/libcurl_la-vauth.o
Removing lib/version_win32.o
Removing lib/vquic/.deps/
Removing lib/vquic/.dirstamp
Removing lib/vquic/libcurl_la-msh3.lo
Removing lib/vquic/libcurl_la-msh3.o
Removing lib/vquic/libcurl_la-ngtcp2.lo
Removing lib/vquic/libcurl_la-ngtcp2.o
Removing lib/vquic/libcurl_la-quiche.lo
Removing lib/vquic/libcurl_la-quiche.o
Removing lib/vquic/libcurl_la-vquic.lo
Removing lib/vquic/libcurl_la-vquic.o
Removing lib/vssh/.deps/
Removing lib/vssh/.dirstamp
Removing lib/vssh/libcurl_la-libssh.lo
Removing lib/vssh/libcurl_la-libssh.o
Removing lib/vssh/libcurl_la-libssh2.lo
Removing lib/vssh/libcurl_la-libssh2.o
Removing lib/vssh/libcurl_la-wolfssh.lo
Removing lib/vssh/libcurl_la-wolfssh.o
Removing lib/vtls/.deps/
Removing lib/vtls/.dirstamp
Removing lib/vtls/libcurl_la-bearssl.lo
Removing lib/vtls/libcurl_la-bearssl.o
Removing lib/vtls/libcurl_la-gskit.lo
Removing lib/vtls/libcurl_la-gskit.o
Removing lib/vtls/libcurl_la-gtls.lo
Removing lib/vtls/libcurl_la-gtls.o
Removing lib/vtls/libcurl_la-hostcheck.lo
Removing lib/vtls/libcurl_la-hostcheck.o
Removing lib/vtls/libcurl_la-keylog.lo
Removing lib/vtls/libcurl_la-keylog.o
Removing lib/vtls/libcurl_la-mbedtls.lo
Removing lib/vtls/libcurl_la-mbedtls.o
Removing lib/vtls/libcurl_la-mbedtls_threadlock.lo
Removing lib/vtls/libcurl_la-mbedtls_threadlock.o
Removing lib/vtls/libcurl_la-nss.lo
Removing lib/vtls/libcurl_la-nss.o
Removing lib/vtls/libcurl_la-openssl.lo
Removing lib/vtls/libcurl_la-openssl.o
Removing lib/vtls/libcurl_la-rustls.lo
Removing lib/vtls/libcurl_la-rustls.o
Removing lib/vtls/libcurl_la-schannel.lo
Removing lib/vtls/libcurl_la-schannel.o
Removing lib/vtls/libcurl_la-schannel_verify.lo
Removing lib/vtls/libcurl_la-schannel_verify.o
Removing lib/vtls/libcurl_la-sectransp.lo
Removing lib/vtls/libcurl_la-sectransp.o
Removing lib/vtls/libcurl_la-vtls.lo
Removing lib/vtls/libcurl_la-vtls.o
Removing lib/vtls/libcurl_la-wolfssl.lo
Removing lib/vtls/libcurl_la-wolfssl.o
Removing lib/vtls/libcurl_la-x509asn1.lo
Removing lib/vtls/libcurl_la-x509asn1.o
Removing lib/warnless.o
Removing libcurl.pc
Removing libtool
Removing ltmain.sh
Removing m4/libtool.m4
Removing m4/ltoptions.m4
Removing m4/ltsugar.m4
Removing m4/ltversion.m4
Removing m4/lt~obsolete.m4
Removing missing
Removing packages/Makefile
Removing packages/Makefile.in
Removing packages/vms/Makefile
Removing packages/vms/Makefile.in
Removing scripts/Makefile
Removing scripts/Makefile.in
Removing src/.deps/
Removing src/.libs/
Removing src/Makefile
Removing src/Makefile.in
Removing src/curl
Removing src/slist_wc.o
Removing src/tool_binmode.o
Removing src/tool_bname.o
Removing src/tool_cb_dbg.o
Removing src/tool_cb_hdr.o
Removing src/tool_cb_prg.o
Removing src/tool_cb_rea.o
Removing src/tool_cb_see.o
Removing src/tool_cb_wrt.o
Removing src/tool_cfgable.o
Removing src/tool_dirhie.o
Removing src/tool_doswin.o
Removing src/tool_easysrc.o
Removing src/tool_filetime.o
Removing src/tool_findfile.o
Removing src/tool_formparse.o
Removing src/tool_getparam.o
Removing src/tool_getpass.o
Removing src/tool_help.o
Removing src/tool_helpers.o
Removing src/tool_hugehelp.c
Removing src/tool_hugehelp.o
Removing src/tool_libinfo.o
Removing src/tool_listhelp.o
Removing src/tool_main.o
Removing src/tool_msgs.o
Removing src/tool_operate.o
Removing src/tool_operhlp.o
Removing src/tool_panykey.o
Removing src/tool_paramhlp.o
Removing src/tool_parsecfg.o
Removing src/tool_progress.o
Removing src/tool_setopt.o
Removing src/tool_sleep.o
Removing src/tool_strdup.o
Removing src/tool_urlglob.o
Removing src/tool_util.o
Removing src/tool_vms.o
Removing src/tool_writeout.o
Removing src/tool_writeout_json.o
Removing src/tool_xattr.o
Removing test-driver
Removing tests/Makefile
Removing tests/Makefile.in
Removing tests/certs/Makefile
Removing tests/certs/Makefile.in
Removing tests/certs/scripts/Makefile
Removing tests/certs/scripts/Makefile.in
Removing tests/configurehelp.pm
Removing tests/data/Makefile
Removing tests/data/Makefile.in
Removing tests/libtest/.deps/
Removing tests/libtest/Makefile
Removing tests/libtest/Makefile.in
Removing tests/server/.deps/
Removing tests/server/Makefile
Removing tests/server/Makefile.in
Removing tests/unit/.deps/
Removing tests/unit/Makefile
Removing tests/unit/Makefile.in
$ autoreconf -fi
libtoolize: putting auxiliary files in '.'.
libtoolize: copying file './ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
libtoolize: copying file 'm4/libtool.m4'
libtoolize: copying file 'm4/ltoptions.m4'
libtoolize: copying file 'm4/ltsugar.m4'
libtoolize: copying file 'm4/ltversion.m4'
libtoolize: copying file 'm4/lt~obsolete.m4'
libtoolize: Remember to add 'LT_INIT' to configure.ac.
configure.ac:123: installing './compile'
configure.ac:333: installing './config.guess'
configure.ac:333: installing './config.sub'
configure.ac:123: installing './install-sh'
configure.ac:129: installing './missing'
docs/examples/Makefile.am: installing './depcomp'
parallel-tests: installing './test-driver'
$ bash -c find . -name config.guess -exec cp /usr/share/misc/config.guess {} \;
$ bash -c find . -name config.sub -exec cp /usr/share/misc/config.sub {} \;
$ chmod +x configure
$ ./configure --disable-shared --without-ssl --disable-docs
checking whether to enable maintainer-specific portions of Makefiles... no
checking whether make supports nested variables... yes
checking whether to enable debug build options... no
checking whether to enable compiler optimizer... (assumed) yes
checking whether to enable strict compiler warnings... no
checking whether to enable compiler warnings as errors... no
checking whether to enable curl debug memory tracking... no
checking whether to enable hiding of library internal symbols... yes
checking whether to enable c-ares for DNS lookups... no
checking whether to disable dependency on -lrt... (assumed no)
checking whether to enable ECH support... no
checking for path separator... :
checking for sed... /usr/bin/sed
checking for grep... /usr/bin/grep
checking that grep -E works... yes
checking for ar... /usr/bin/ar
checking for a BSD-compatible install... /usr/bin/install -c
checking for gcc... ccache gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether the compiler supports GNU C... yes
checking whether ccache gcc accepts -g... yes
checking for ccache gcc option to enable C11 features... none needed
checking whether ccache gcc understands -c and -o together... yes
checking how to run the C preprocessor... ccache gcc -E
checking for stdio.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for strings.h... yes
checking for sys/stat.h... yes
checking for sys/types.h... yes
checking for unistd.h... yes
checking for stdatomic.h... yes
checking if _Atomic is available... yes
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for code coverage support... no
checking whether build environment is sane... yes
checking for a race-free mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of ccache gcc... gcc3
checking curl version... 7.87.0-DEV
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for grep that handles long lines and -e... (cached) /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking if OS is AIX (to define _ALL_SOURCE)... no
checking if _THREAD_SAFE is already defined... no
checking if _THREAD_SAFE is actually needed... no
checking if _THREAD_SAFE is onwards defined... no
checking if _REENTRANT is already defined... no
checking if _REENTRANT is actually needed... no
checking if _REENTRANT is onwards defined... no
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... no
checking how to print strings... printf
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for fgrep... /usr/bin/grep -F
checking for ld used by ccache gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from ccache gcc object... ok
checking for sysroot... no
checking for a working dd... /usr/bin/dd
checking how to truncate binary pipes... /usr/bin/dd bs=4096 count=1
checking for mt... mt
checking if mt is a manifest tool... no
checking for dlfcn.h... yes
checking for objdir... .libs
checking if ccache gcc supports -fno-rtti -fno-exceptions... no
checking for ccache gcc option to produce PIC... -fPIC -DPIC
checking if ccache gcc PIC flag -fPIC -DPIC works... yes
checking if ccache gcc static flag -static works... yes
checking if ccache gcc supports -c -o file.o... yes
checking if ccache gcc supports -c -o file.o... (cached) yes
checking whether the ccache gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... no
checking whether to build static libraries... yes
checking whether to build shared libraries with -version-info... yes
checking whether to build shared libraries with -no-undefined... no
checking whether to build shared libraries with -mimpure-text... no
checking whether to build shared libraries with PIC... yes
checking whether to build static libraries with PIC... yes
checking whether to build shared libraries only... no
checking whether to build static libraries only... yes
checking for windres... no
checking for inline... inline
checking if cpp -P is needed... yes
checking if cpp -P works... yes
checking if compiler is DEC/Compaq/HP C... no
checking if compiler is HP-UX C... no
checking if compiler is IBM C... no
checking if compiler is Intel C... no
checking if compiler is clang... no
checking if compiler is GNU C... yes
checking if compiler is LCC... no
checking if compiler is SGI MIPSpro C... no
checking if compiler is SGI MIPS C... no
checking if compiler is SunPro C... no
checking if compiler is Tiny C... no
checking if compiler accepts some basic options... yes
configure: compiler options added: -Werror-implicit-function-declaration
checking if compiler optimizer assumed setting might be used... yes
checking if compiler accepts optimizer enabling options... yes
configure: compiler options added: -O2
checking if compiler accepts strict warning options... yes
configure: compiler options added: -Wno-system-headers
checking if compiler halts on compilation errors... yes
checking if compiler halts on negative sized arrays... yes
checking if compiler halts on function prototype mismatch... yes
checking if compiler supports hiding library internal symbols... yes
checking for windows.h... no
checking whether build target is a native Windows one... no
checking whether build target supports WIN32 file API... no
checking whether build target supports WIN32 crypto API... no
checking for good-to-use Darwin CFLAGS... no
checking whether to link macOS CoreFoundation and SystemConfiguration framework... no
checking to see if the compiler supports __builtin_available()... no
checking whether to support http... yes
checking whether to support ftp... yes
checking whether to support file... yes
checking whether to support ldap... yes
checking whether to support ldaps... yes
checking whether to support rtsp... yes
checking whether to support proxies... yes
checking whether to support dict... yes
checking whether to support telnet... yes
checking whether to support tftp... yes
checking whether to support pop3... yes
checking whether to support imap... yes
checking whether to support smb... yes
checking whether to support smtp... yes
checking whether to support gopher... yes
checking whether to support mqtt... no
checking whether to provide built-in manual... yes
checking whether to enable generation of C code... yes
checking whether to use libgcc... no
checking if X/Open network library is required... no
checking for gethostbyname... yes
checking for windows.h... (cached) no
checking for winsock2.h... (cached) no
checking for proto/bsdsocket.h... no
checking for connect in libraries... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... yes
checking for monotonic clock_gettime... yes
checking for clock_gettime in libraries... no additional lib required
checking if monotonic clock_gettime works... yes
checking for pkg-config... /usr/bin/pkg-config
checking for zlib options with pkg-config... found
checking for zlib.h... yes
configure: found both libz and libz.h header
checking for BrotliDecoderDecompress in -lbrotlidec... no
checking for brotli/decode.h... no
checking for ZSTD_createDStream in -lzstd... no
checking for zstd.h... no
checking for lber.h... no
checking for ldap.h... no
checking for ldap_ssl.h... no
checking for LDAP libraries... cannot find LDAP libraries
checking whether to enable IPv6... yes
checking if struct sockaddr_in6 has sin6_scope_id member... yes
checking if argv can be written to... yes
checking if GSS-API support is requested... no
checking whether to enable Windows native SSL/TLS... no
checking whether to enable Secure Transport... no
checking whether to enable Amiga native SSL/TLS (AmiSSL v5)... no
checking for library containing psl_builtin... -lpsl
checking for library containing gsasl_init... no
checking for RTMP_Init in -lrtmp... no
checking whether versioned symbols are wanted... no
checking whether to enable Windows native IDN (Windows native builds only)... no
checking whether to build with libidn2... (assumed) yes
checking for pkg-config... (cached) /usr/bin/pkg-config
checking for libidn2 options with pkg-config... no
configure: IDN_LIBS: "-lidn2"
configure: IDN_LDFLAGS: ""
configure: IDN_CPPFLAGS: ""
configure: IDN_DIR: ""
checking if idn2_lookup_ul can be linked... no
checking for idn2.h... no
checking for pkg-config... (cached) /usr/bin/pkg-config
checking for libnghttp2 options with pkg-config... no
checking for nghttp2_session_set_local_window_size in -lnghttp2... no
checking for pkg-config... (cached) /usr/bin/pkg-config
checking for fish options with pkg-config... no
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking for sys/select.h... yes
checking for sys/socket.h... yes
checking for sys/ioctl.h... yes
checking for sys/uio.h... yes
checking for assert.h... yes
checking for unistd.h... (cached) yes
checking for stdlib.h... (cached) yes
checking for arpa/inet.h... yes
checking for net/if.h... yes
checking for netinet/in.h... yes
checking for netinet/in6.h... no
checking for sys/un.h... yes
checking for linux/tcp.h... yes
checking for netinet/tcp.h... yes
checking for netinet/udp.h... yes
checking for netdb.h... yes
checking for sys/sockio.h... no
checking for sys/stat.h... (cached) yes
checking for sys/param.h... yes
checking for termios.h... yes
checking for termio.h... yes
checking for fcntl.h... yes
checking for io.h... no
checking for pwd.h... yes
checking for utime.h... yes
checking for sys/utime.h... no
checking for sys/poll.h... yes
checking for poll.h... yes
checking for socket.h... no
checking for sys/resource.h... yes
checking for libgen.h... yes
checking for locale.h... yes
checking for errno.h... yes
checking for stdbool.h... yes
checking for arpa/tftp.h... yes
checking for sys/filio.h... no
checking for sys/wait.h... yes
checking for setjmp.h... yes
checking for an ANSI C-conforming const... yes
checking for compiler support of C99 variadic macro style... yes
checking for compiler support of old gcc variadic macro style... yes
checking for size_t... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for struct timeval... yes
checking run-time libs availability... fine
checking size of size_t... 8
checking size of long... 8
checking size of int... 4
checking size of time_t... 8
checking size of off_t... 8
checking size of curl_off_t... 8
checking for long long... yes
checking for ssize_t... yes
checking for bool... yes
checking for sa_family_t... yes
checking for suseconds_t... yes
checking if time_t is unsigned... no
checking for sys/types.h... (cached) yes
checking for poll.h... (cached) yes
checking for sys/poll.h... (cached) yes
checking for in_addr_t... yes
checking for struct sockaddr_storage... yes
checking for sys/select.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for select... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for recv... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for send... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for MSG_NOSIGNAL... yes
checking for sys/types.h... (cached) yes
checking for unistd.h... (cached) yes
checking if alarm can be linked... yes
checking if alarm is prototyped... yes
checking if alarm is compilable... yes
checking if alarm usage allowed... yes
checking if alarm might be used... yes
checking for sys/types.h... (cached) yes
checking for string.h... (cached) yes
checking for strings.h... (cached) yes
checking for sys/types.h... (cached) yes
checking for libgen.h... (cached) yes
checking if basename can be linked... yes
checking if basename is prototyped... yes
checking if basename is compilable... yes
checking if basename usage allowed... yes
checking if basename might be used... yes
checking for sys/types.h... (cached) yes
checking for socket.h... (cached) no
checking if closesocket can be linked... no
checking if closesocket might be used... no
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking if CloseSocket can be linked... no
checking if CloseSocket might be used... no
checking if connect can be linked... yes
checking if connect is prototyped... yes
checking if connect is compilable... yes
checking if connect usage allowed... yes
checking if connect might be used... yes
checking for sys/types.h... (cached) yes
checking for unistd.h... (cached) yes
checking for fcntl.h... (cached) yes
checking if fcntl can be linked... yes
checking if fcntl is prototyped... yes
checking if fcntl is compilable... yes
checking if fcntl usage allowed... yes
checking if fcntl might be used... yes
checking if fcntl O_NONBLOCK is compilable... yes
checking if fcntl O_NONBLOCK usage allowed... yes
checking if fcntl O_NONBLOCK might be used... yes
checking for windows.h... (cached) no
checking for winsock2.h... (cached) no
checking for ws2tcpip.h... (cached) no
checking for sys/types.h... (cached) yes
checking for netdb.h... (cached) yes
checking if freeaddrinfo can be linked... yes
checking if freeaddrinfo is prototyped... yes
checking if freeaddrinfo is compilable... yes
checking if freeaddrinfo usage allowed... yes
checking if freeaddrinfo might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/xattr.h... yes
checking if fsetxattr can be linked... yes
checking if fsetxattr is prototyped... yes
checking if fsetxattr takes 5 args.... yes
checking if fsetxattr is compilable... yes
checking if fsetxattr usage allowed... yes
checking if fsetxattr might be used... yes
checking if ftruncate can be linked... yes
checking if ftruncate is prototyped... yes
checking if ftruncate is compilable... yes
checking if ftruncate usage allowed... yes
checking if ftruncate might be used... yes
checking for sys/types.h... (cached) yes
checking for stdlib.h... (cached) yes
checking if getaddrinfo can be linked... yes
checking if getaddrinfo is prototyped... yes
checking if getaddrinfo is compilable... yes
checking if getaddrinfo seems to work... yes
checking if getaddrinfo usage allowed... yes
checking if getaddrinfo might be used... yes
checking if getaddrinfo is threadsafe... yes
checking if gethostbyname can be linked... yes
checking if gethostbyname is prototyped... yes
checking if gethostbyname is compilable... yes
checking if gethostbyname usage allowed... yes
checking if gethostbyname might be used... yes
checking if gethostbyname_r can be linked... yes
checking if gethostbyname_r is prototyped... yes
checking if gethostbyname_r takes 3 args.... no
checking if gethostbyname_r takes 5 args.... no
checking if gethostbyname_r takes 6 args.... yes
checking if gethostbyname_r is compilable... yes
checking if gethostbyname_r usage allowed... yes
checking if gethostbyname_r might be used... yes
checking if gethostname can be linked... yes
checking if gethostname is prototyped... yes
checking if gethostname is compilable... yes
checking for gethostname arg 2 data type... size_t
checking if gethostname usage allowed... yes
checking if gethostname might be used... yes
checking if getpeername can be linked... yes
checking if getpeername is prototyped... yes
checking if getpeername is compilable... yes
checking if getpeername usage allowed... yes
checking if getpeername might be used... yes
checking if getsockname can be linked... yes
checking if getsockname is prototyped... yes
checking if getsockname is compilable... yes
checking if getsockname usage allowed... yes
checking if getsockname might be used... yes
checking for net/if.h... (cached) yes
checking if if_nametoindex can be linked... yes
checking if if_nametoindex is prototyped... yes
checking if if_nametoindex is compilable... yes
checking if if_nametoindex usage allowed... yes
checking if if_nametoindex might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for netinet/in.h... (cached) yes
checking for ifaddrs.h... yes
checking if getifaddrs can be linked... yes
checking if getifaddrs is prototyped... yes
checking if getifaddrs is compilable... yes
checking if getifaddrs seems to work... yes
checking if getifaddrs usage allowed... yes
checking if getifaddrs might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking if gmtime_r can be linked... yes
checking if gmtime_r is prototyped... yes
checking if gmtime_r is compilable... yes
checking if gmtime_r seems to work... yes
checking if gmtime_r usage allowed... yes
checking if gmtime_r might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for netinet/in.h... (cached) yes
checking for arpa/inet.h... (cached) yes
checking if inet_ntop can be linked... yes
checking if inet_ntop is prototyped... yes
checking if inet_ntop is compilable... yes
checking if inet_ntop seems to work... yes
checking if inet_ntop usage allowed... yes
checking if inet_ntop might be used... yes
checking if inet_pton can be linked... yes
checking if inet_pton is prototyped... yes
checking if inet_pton is compilable... yes
checking if inet_pton seems to work... yes
checking if inet_pton usage allowed... yes
checking if inet_pton might be used... yes
checking if ioctlsocket can be linked... no
checking if ioctlsocket might be used... no
checking if IoctlSocket can be linked... no
checking if IoctlSocket might be used... no
checking if memrchr can be linked... yes
checking if memrchr is prototyped... no
checking if memrchr might be used... no
checking if poll can be linked... yes
checking if poll is prototyped... yes
checking if poll is compilable... yes
checking if poll seems to work... yes
checking if poll usage allowed... yes
checking if poll might be used... yes
checking for sys/types.h... (cached) yes
checking for signal.h... yes
checking if sigaction can be linked... yes
checking if sigaction is prototyped... yes
checking if sigaction is compilable... yes
checking if sigaction usage allowed... yes
checking if sigaction might be used... yes
checking if siginterrupt can be linked... yes
checking if siginterrupt is prototyped... yes
checking if siginterrupt is compilable... yes
checking if siginterrupt usage allowed... yes
checking if siginterrupt might be used... yes
checking if signal can be linked... yes
checking if signal is prototyped... yes
checking if signal is compilable... yes
checking if signal usage allowed... yes
checking if signal might be used... yes
checking for sys/types.h... (cached) yes
checking for setjmp.h... (cached) yes
checking if sigsetjmp can be linked... no
checking if sigsetjmp seems a macro... yes
checking if sigsetjmp is compilable... yes
checking if sigsetjmp usage allowed... yes
checking if sigsetjmp might be used... yes
checking if socket can be linked... yes
checking if socket is prototyped... yes
checking if socket is compilable... yes
checking if socket usage allowed... yes
checking if socket might be used... yes
checking if socketpair can be linked... yes
checking if socketpair is prototyped... yes
checking if socketpair is compilable... yes
checking if socketpair usage allowed... yes
checking if socketpair might be used... yes
checking if strcasecmp can be linked... yes
checking if strcasecmp is prototyped... yes
checking if strcasecmp is compilable... yes
checking if strcasecmp usage allowed... yes
checking if strcasecmp might be used... yes
checking if strcmpi can be linked... no
checking if strcmpi might be used... no
checking if strdup can be linked... yes
checking if strdup is prototyped... yes
checking if strdup is compilable... yes
checking if strdup usage allowed... yes
checking if strdup might be used... yes
checking if strerror_r can be linked... yes
checking if strerror_r is prototyped... yes
checking if strerror_r is compilable... yes
checking if strerror_r is glibc like... no
checking if strerror_r is POSIX like... yes
checking if strerror_r seems to work... yes
checking if strerror_r usage allowed... yes
checking if strerror_r might be used... yes
checking if stricmp can be linked... no
checking if stricmp might be used... no
checking if strtok_r can be linked... yes
checking if strtok_r is prototyped... yes
checking if strtok_r is compilable... yes
checking if strtok_r usage allowed... yes
checking if strtok_r might be used... yes
checking if strtoll can be linked... yes
checking if strtoll is prototyped... yes
checking if strtoll is compilable... yes
checking if strtoll usage allowed... yes
checking if strtoll might be used... yes
checking for ccache gcc options needed to detect all undeclared functions... none needed
checking whether getpwuid_r is declared... yes
checking for fnmatch... yes
checking for fchmod... yes
checking for fork... yes
checking for geteuid... yes
checking for getpass_r... no
checking deeper for getpass_r... but still no
checking for getppid... yes
checking for getpwuid... yes
checking for getpwuid_r... yes
checking for getrlimit... yes
checking for gettimeofday... yes
checking for if_nametoindex... yes
checking for mach_absolute_time... no
checking deeper for mach_absolute_time... but still no
checking for pipe... yes
checking for sched_yield... yes
checking for sendmsg... yes
checking for setlocale... yes
checking for setmode... no
checking deeper for setmode... but still no
checking for setrlimit... yes
checking for snprintf... yes
checking for utime... yes
checking for utimes... yes
checking how to set a socket into non-blocking mode... fcntl O_NONBLOCK
checking for perl... /usr/bin/perl
checking for gnroff... no
checking for nroff... /usr/bin/nroff
checking how to use *nroff to get plain text from man pages... -man
checking whether to enable the threaded resolver... yes
checking whether to use POSIX threads for threaded resolver... auto
checking for pthread.h... yes
checking for pthread_create... yes
checking convert -I options to -isystem... yes
checking whether to enable verbose strings... yes
checking whether to enable SSPI support (Windows native builds only)... no
checking whether to enable cryptographic authentication methods... yes
checking whether to support NTLM... yes
checking whether to enable NTLM delegation to winbind's helper... no
checking whether to enable TLS-SRP authentication... yes
checking whether to enable Unix domain sockets... auto
checking for struct sockaddr_un.sun_path... yes
checking whether to support cookies... yes
checking whether to support socketpair... yes
checking whether to support HTTP authentication... yes
checking whether to support DoH... yes
checking whether to support the MIME API... yes
checking whether to support date parsing... yes
checking whether to support netrc parsing... yes
checking whether to support progress-meter... yes
checking whether to support DNS shuffling... yes
checking whether to support curl_easy_option*... yes
checking whether to support alt-svc... yes
checking whether to support headers-api... yes
configure: disables HSTS due to lack of SSL
checking whether to support WebSockets... no
checking whether hiding of library internal symbols will actually happen... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating docs/Makefile
config.status: creating docs/examples/Makefile
config.status: creating docs/libcurl/Makefile
config.status: creating docs/libcurl/opts/Makefile
config.status: creating docs/cmdline-opts/Makefile
config.status: creating include/Makefile
config.status: creating include/curl/Makefile
config.status: creating src/Makefile
config.status: creating lib/Makefile
config.status: creating scripts/Makefile
config.status: creating lib/libcurl.vers
config.status: creating lib/libcurl.plist
config.status: creating tests/Makefile
config.status: creating tests/certs/Makefile
config.status: creating tests/certs/scripts/Makefile
config.status: creating tests/data/Makefile
config.status: creating tests/server/Makefile
config.status: creating tests/libtest/Makefile
config.status: creating tests/unit/Makefile
config.status: creating packages/Makefile
config.status: creating packages/vms/Makefile
config.status: creating curl-config
config.status: creating libcurl.pc
config.status: creating lib/curl_config.h
config.status: executing depfiles commands
config.status: executing libtool commands
configure: amending lib/Makefile
configure: amending src/Makefile
configure: amending tests/unit/Makefile
configure: amending tests/server/Makefile
configure: amending tests/libtest/Makefile
configure: amending docs/examples/Makefile
configure: Configured to build curl/libcurl:
Host setup: x86_64-pc-linux-gnu
Install prefix: /usr/local
Compiler: ccache gcc
CFLAGS: -Werror-implicit-function-declaration -O2 -Wno-system-headers
CPPFLAGS:
LDFLAGS:
LIBS: -lpsl -lz
curl version: 7.87.0-DEV
SSL: no (--with-{openssl,gnutls,nss,mbedtls,wolfssl,schannel,secure-transport,amissl,bearssl,rustls} )
SSH: no (--with-{libssh,libssh2})
zlib: enabled
brotli: no (--with-brotli)
zstd: no (--with-zstd)
GSS-API: no (--with-gssapi)
GSASL: no (libgsasl not found)
TLS-SRP: no (--enable-tls-srp)
resolver: POSIX threaded
IPv6: enabled
Unix sockets: enabled
IDN: no (--with-{libidn2,winidn})
Build libcurl: Shared=no, Static=yes
Built-in manual: enabled
--libcurl option: enabled (--disable-libcurl-option)
Verbose errors: enabled (--disable-verbose)
Code coverage: disabled
SSPI: no (--enable-sspi)
ca cert bundle: no
ca cert path:
ca fallback:
LDAP: no (--enable-ldap / --with-ldap-lib / --with-lber-lib)
LDAPS: no (--enable-ldaps)
RTSP: enabled
RTMP: no (--with-librtmp)
PSL: enabled
Alt-svc: enabled (--disable-alt-svc)
Headers API: enabled (--disable-headers-api)
HSTS: no (--enable-hsts)
HTTP1: enabled (internal)
HTTP2: no (--with-nghttp2, --with-hyper)
HTTP3: no (--with-ngtcp2, --with-quiche --with-msh3)
ECH: no (--enable-ech)
WebSockets: no (--enable-websockets)
Protocols: DICT FILE FTP GOPHER HTTP IMAP MQTT POP3 RTSP SMTP TELNET TFTP
Features: AsynchDNS IPv6 Largefile PSL UnixSockets alt-svc libz threadsafe
configure: WARNING: unrecognized options: --disable-docs
configure: WARNING: Cannot find libraries for LDAP support: LDAP disabled
configure: WARNING: libgsasl was not found
configure: WARNING: Cannot find libraries for IDN support: IDN disabled
configure: WARNING: unrecognized options: --disable-docs
$ make -j3
Making all in lib
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
make all-am
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
CC libcurl_la-altsvc.lo
CC libcurl_la-amigaos.lo
CC libcurl_la-asyn-ares.lo
CC libcurl_la-asyn-thread.lo
CC libcurl_la-base64.lo
CC libcurl_la-bufref.lo
CC libcurl_la-c-hyper.lo
CC libcurl_la-cfilters.lo
CC libcurl_la-conncache.lo
CC libcurl_la-connect.lo
CC libcurl_la-content_encoding.lo
CC libcurl_la-cookie.lo
CC libcurl_la-curl_addrinfo.lo
CC libcurl_la-curl_des.lo
CC libcurl_la-curl_endian.lo
CC libcurl_la-curl_fnmatch.lo
CC libcurl_la-curl_get_line.lo
CC libcurl_la-curl_gethostname.lo
CC libcurl_la-curl_gssapi.lo
CC libcurl_la-curl_memrchr.lo
CC libcurl_la-curl_multibyte.lo
CC libcurl_la-curl_ntlm_core.lo
CC libcurl_la-curl_ntlm_wb.lo
CC libcurl_la-curl_path.lo
CC libcurl_la-curl_range.lo
CC libcurl_la-curl_rtmp.lo
CC libcurl_la-curl_sasl.lo
CC libcurl_la-curl_sspi.lo
CC libcurl_la-curl_threads.lo
CC libcurl_la-dict.lo
CC libcurl_la-doh.lo
CC libcurl_la-dynbuf.lo
CC libcurl_la-easy.lo
CC libcurl_la-easygetopt.lo
CC libcurl_la-easyoptions.lo
CC libcurl_la-escape.lo
CC libcurl_la-file.lo
CC libcurl_la-fileinfo.lo
CC libcurl_la-fopen.lo
CC libcurl_la-formdata.lo
CC libcurl_la-ftp.lo
CC libcurl_la-ftplistparser.lo
CC libcurl_la-getenv.lo
CC libcurl_la-getinfo.lo
CC libcurl_la-gopher.lo
CC libcurl_la-h2h3.lo
CC libcurl_la-hash.lo
CC libcurl_la-headers.lo
CC libcurl_la-hmac.lo
CC libcurl_la-hostasyn.lo
CC libcurl_la-hostip.lo
CC libcurl_la-hostip4.lo
CC libcurl_la-hostip6.lo
CC libcurl_la-hostsyn.lo
CC libcurl_la-hsts.lo
CC libcurl_la-http.lo
CC libcurl_la-http2.lo
CC libcurl_la-http_chunks.lo
CC libcurl_la-http_digest.lo
CC libcurl_la-http_negotiate.lo
CC libcurl_la-http_ntlm.lo
CC libcurl_la-http_proxy.lo
CC libcurl_la-http_aws_sigv4.lo
CC libcurl_la-idn_win32.lo
CC libcurl_la-if2ip.lo
CC libcurl_la-imap.lo
CC libcurl_la-inet_ntop.lo
CC libcurl_la-inet_pton.lo
CC libcurl_la-krb5.lo
CC libcurl_la-ldap.lo
CC libcurl_la-llist.lo
CC libcurl_la-md4.lo
CC libcurl_la-md5.lo
CC libcurl_la-memdebug.lo
CC libcurl_la-mime.lo
CC libcurl_la-mprintf.lo
CC libcurl_la-mqtt.lo
CC libcurl_la-multi.lo
CC libcurl_la-netrc.lo
CC libcurl_la-nonblock.lo
CC libcurl_la-noproxy.lo
CC libcurl_la-openldap.lo
CC libcurl_la-parsedate.lo
CC libcurl_la-pingpong.lo
CC libcurl_la-pop3.lo
CC libcurl_la-progress.lo
CC libcurl_la-psl.lo
CC libcurl_la-rand.lo
CC libcurl_la-rename.lo
CC libcurl_la-rtsp.lo
CC libcurl_la-select.lo
CC libcurl_la-sendf.lo
CC libcurl_la-setopt.lo
CC libcurl_la-sha256.lo
CC libcurl_la-share.lo
CC libcurl_la-slist.lo
CC libcurl_la-smb.lo
CC libcurl_la-smtp.lo
CC libcurl_la-socketpair.lo
CC libcurl_la-socks.lo
CC libcurl_la-socks_gssapi.lo
CC libcurl_la-socks_sspi.lo
CC libcurl_la-speedcheck.lo
CC libcurl_la-splay.lo
CC libcurl_la-strcase.lo
CC libcurl_la-strdup.lo
CC libcurl_la-strerror.lo
CC libcurl_la-strtok.lo
CC libcurl_la-strtoofft.lo
CC libcurl_la-system_win32.lo
CC libcurl_la-telnet.lo
CC libcurl_la-tftp.lo
CC libcurl_la-timediff.lo
CC libcurl_la-timeval.lo
CC libcurl_la-transfer.lo
CC libcurl_la-url.lo
CC libcurl_la-urlapi.lo
CC libcurl_la-version.lo
CC libcurl_la-version_win32.lo
CC libcurl_la-warnless.lo
CC libcurl_la-wildcard.lo
CC libcurl_la-ws.lo
CC vauth/libcurl_la-cleartext.lo
CC vauth/libcurl_la-cram.lo
CC vauth/libcurl_la-digest.lo
CC vauth/libcurl_la-digest_sspi.lo
CC vauth/libcurl_la-gsasl.lo
CC vauth/libcurl_la-krb5_gssapi.lo
CC vauth/libcurl_la-krb5_sspi.lo
CC vauth/libcurl_la-ntlm.lo
CC vauth/libcurl_la-ntlm_sspi.lo
CC vauth/libcurl_la-oauth2.lo
CC vauth/libcurl_la-spnego_gssapi.lo
CC vauth/libcurl_la-spnego_sspi.lo
CC vauth/libcurl_la-vauth.lo
CC vtls/libcurl_la-bearssl.lo
CC vtls/libcurl_la-gskit.lo
CC vtls/libcurl_la-gtls.lo
CC vtls/libcurl_la-hostcheck.lo
CC vtls/libcurl_la-keylog.lo
CC vtls/libcurl_la-mbedtls.lo
CC vtls/libcurl_la-mbedtls_threadlock.lo
CC vtls/libcurl_la-nss.lo
CC vtls/libcurl_la-openssl.lo
CC vtls/libcurl_la-rustls.lo
CC vtls/libcurl_la-schannel.lo
CC vtls/libcurl_la-schannel_verify.lo
CC vtls/libcurl_la-sectransp.lo
CC vtls/libcurl_la-vtls.lo
CC vtls/libcurl_la-wolfssl.lo
CC vtls/libcurl_la-x509asn1.lo
CC vquic/libcurl_la-msh3.lo
CC vquic/libcurl_la-ngtcp2.lo
CC vquic/libcurl_la-quiche.lo
CC vquic/libcurl_la-vquic.lo
CC vssh/libcurl_la-libssh.lo
CC vssh/libcurl_la-libssh2.lo
CC vssh/libcurl_la-wolfssh.lo
CCLD libcurl.la
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
Making all in src
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/src'
Making all in ../docs
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
Making all in .
make[3]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
if test "..x" != "..x" -a -e "./curl.1"; then \
/usr/bin/install -c -m 644 "./curl.1" /root/asi_db/builds/curl/_worktrees/w0/docs/curl.1; fi
cd cmdline-opts && make
make[4]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
generate ../../docs/curl.1
make[4]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
make[3]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
Making all in cmdline-opts
make[3]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
make[3]: Nothing to be done for 'all'.
make[3]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/src'
CC tool_binmode.o
CC tool_bname.o
CC slist_wc.o
CC tool_cb_dbg.o
CC tool_cb_hdr.o
CC tool_cb_prg.o
CC tool_cb_rea.o
CC tool_cb_see.o
CC tool_cb_wrt.o
CC tool_cfgable.o
CC tool_dirhie.o
CC tool_doswin.o
CC tool_easysrc.o
CC tool_filetime.o
CC tool_findfile.o
CC tool_getparam.o
CC tool_getpass.o
CC tool_formparse.o
CC tool_help.o
CC tool_helpers.o
HUGE tool_hugehelp.c
CC tool_libinfo.o
CC tool_listhelp.o
CC tool_main.o
CC tool_msgs.o
CC tool_operate.o
CC tool_operhlp.o
CC tool_paramhlp.o
CC tool_panykey.o
CC tool_parsecfg.o
CC tool_progress.o
CC tool_strdup.o
CC tool_setopt.o
CC tool_sleep.o
CC tool_urlglob.o
CC tool_util.o
CC tool_vms.o
CC tool_writeout.o
CC tool_writeout_json.o
CC tool_xattr.o
CC ../lib/strtoofft.o
CC ../lib/timediff.o
CC ../lib/nonblock.o
CC ../lib/warnless.o
CC ../lib/curl_multibyte.o
CC ../lib/version_win32.o
CC ../lib/dynbuf.o
CC tool_hugehelp.o
CCLD curl
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/src'
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/src'
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0'
| @@ -0,0 +1,127 @@ | |
| + | # curl connection filters |
| + | |
| + | Connection filters is a design in the internals of curl, not visible in its public API. They were added |
| + | in curl v7.xx.x. This document describes the concepts, its high level implementation and the motivations. |
| + | |
| + | ## Filters |
| + | |
| + | A "connection filter" is a piece of code that is responsible for handling a range of operations |
| + | of curl's connections: reading, writing, waiting on external events, connecting and closing down - to name the most important ones. |
| + | |
| + | The most important feat of connection filters is that they can be stacked on top of each other (or "chained" if you prefer that metaphor). In the common scenario that you want to retrieve a `https:` url with curl, you need 2 basic things to send the request and get the response: a TCP connection, represented by a `socket` and a SSL instance en- and decrypt over that socket. You write your request to the SSL instance, which encrypts and writes that data to the socket, which then sends the bytes over the network. |
| + | |
| + | With connection filters, curl's internal setup will look something like this (cf for connection filter): |
| + | |
| + | ``` |
| + | Curl_easy *data connectdata *conn cf-ssl cf-socket |
| + | +----------------+ +-----------------+ +-------+ +--------+ |
| + | |https://curl.se/|----> | properties |----> | keys |---> | socket |--> OS --> network |
| + | +----------------+ +-----------------+ +-------+ +--------+ |
| + | |
| + | Curl_write(data, buffer) |
| + | --> Curl_cfilter_write(data, data->conn, buffer) |
| + | ---> conn->filter->write(conn->filter, data, buffer) |
| + | ``` |
| + | |
| + | While connection filters all do different things, they look the same from the "outside". The code in `data` and `conn` does not really know **which** filters are installed. `conn` just writes into the first filter, whatever that is. |
| + | |
| + | Same is true for filters. Each filter has a pointer to the `next` filter. When SSL has encrypted the data, it does not write to a socket, it writes to the next filter. If that is indeed a socket, or a file, or a HTTP/2 connection is of no concern to the SSL filter. |
| + | |
| + | And this allows the stacking, as in: |
| + | |
| + | ``` |
| + | Direct: |
| + | http://localhost/ conn -> cf-socket |
| + | https://curl.se/ conn -> cf-ssl -> cf-socket |
| + | Via http proxy tunnel: |
| + | http://localhost/ conn -> cf-http-proxy -> cf-socket |
| + | https://curl.se/ conn -> cf-ssl -> cf-http-proxy -> cf-socket |
| + | Via https proxy tunnel: |
| + | http://localhost/ conn -> cf-http-proxy -> cf-ssl -> cf-socket |
| + | https://curl.se/ conn -> cf-ssl -> cf-http-proxy -> cf-ssl -> cf-socket |
| + | Via http proxy tunnel via SOCKS proxy: |
| + | http://localhost/ conn -> cf-http-proxy -> cf-socks -> cf-socket |
| + | ``` |
| + | |
| + | ### Connecting/Closing |
| + | |
| + | Before `Curl_easy` can send the request, the connection needs to be established. This means that all connection filters have done, whatever they need to do: waiting for the socket to be connected, doing the TLS handshake, performing the HTTP tunnel request, etc. This has to be done in reverse order: the last filter has to do its connect first, then the one above can start, etc. |
| + | |
| + | Each filter does in principle the following: |
| + | |
| + | ``` |
| + | static CURLcode |
| + | myfilter_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool *done) |
| + | { |
| + | CURLcode result; |
| + | |
| + | if(cf->connected) { /* we and all below are done */ |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | /* Let the filters below connect */ |
| + | result = cf->next->cft->connect(cf->next, data, blocking, done); |
| + | if(result || !*done) |
| + | return result; /* below errored/not finished yet */ |
| + | |
| + | /* MYFILTER CONNECT THINGS */ /* below connected, do out thing */ |
| + | *done = cf->connected = TRUE; /* done, remember, return */ |
| + | return CURLE_OK; |
| + | } |
| + | ``` |
| + | |
| + | Closing a connection then works similar. The `conn` tells the first filter to close. Contrary to connecting, |
| + | the filter does its own things first, before telling the next filter to close. |
| + | |
| + | ### Efficiency |
| + | |
| + | There are two things curl is concerned about: efficient memory use and fast transfers. |
| + | |
| + | The memory footprint of a filter is relatively small: |
| + | |
| + | ``` |
| + | struct Curl_cfilter { |
| + | const struct Curl_cftype *cft; /* the type providing implementation */ |
| + | 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 */ |
| + | BIT(connected); /* != 0 iff this filter is connected */ |
| + | }; |
| + | ``` |
| + | The filter type `cft` is a singleton, one static struct for each type of filter. The `ctx` is where a filter will hold its specific data. That varies by filter type. A http-proxy filter will keep the ongoing state of the CONNECT here, but free it after its has been established. The SSL filter will keep the `SSL*` (if OpenSSL is used) here until the connection is closed. So, this varies. |
| + | |
| + | `conn` is a reference to the connection this filter belongs to, so nothing extra besides the pointer itself. |
| + | |
| + | Several things, that before were kept in `struct connectdata`, will now go into the `filter->ctx` *when needed*. So, the memory footprint for connections that do *not* use a http proxy, or socks, or https will be lower. |
| + | |
| + | As to transfer efficiency, writing and reading through a filter comes at near zero cost *if the filter does not transform the data*. A http proxy or socks filter, once it is connected, will just pass the calls through. Those filters implementations will look like this: |
| + | |
| + | ``` |
| + | ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, |
| + | const void *buf, size_t len, CURLcode *err) |
| + | { |
| + | return cf->next->cft->do_send(cf->next, data, buf, len, err); |
| + | } |
| + | ``` |
| + | The `recv` implementation is equivalent. |
| + | |
| + | ## Filter Types |
| + | |
| + | The (currently) existing filter types are: SOCKET, SOCKET-ACCEPT, SSL, HTTP-PROXY and SOCKS-PROXY. Vital to establishing and read/writing a connection. But filters are also a good way to implement tasks for *managing* a connection: |
| + | |
| + | * **Statistics**: a filter that counts the number of bytes sent/received. Place one in front of SOCKET and one higher up and get the number of raw and "easy" bytes transferred. They may track the speed as well, or number of partial writes, etc. |
| + | * **Timeout**: enforce timeouts, e.g. fail if a connection cannot be established in a certain amount of time. |
| + | * **Progress**: report progress on a connection. |
| + | * **Pacing**: limit read/write rates. |
| + | * **Testing**: simulate network condition or failures. |
| + | |
| + | As you see, filters are a good way to add functionality to curl's internal handling of transfers without impact on other code. |
| + | |
| + | ## Easy Filters? |
| + | |
| + | Some things that curl needs to manage are not directly tied to a specific connection but the property of the `Curl_easy` handle, e.g. a particular transfer. When using HTTP/2 or HTTP/3, many transfers can use the same connection. If one wants to monitor of the transfer itself or restricting its speed alone, a connection filter is not the right place to do this. |
| + | |
| + | So we might add "easy filters" one day. Who knows? |
| @@ -55,6 +55,7 @@ EXTRA_DIST = \ | |
| CODE_OF_CONDUCT.md \ | |
| CODE_REVIEW.md \ | |
| CODE_STYLE.md \ | |
| + | CONNECTION-FILTERS.md \ |
| CONTRIBUTE.md \ | |
| CURL-DISABLE.md \ | |
| DEPRECATE.md \ | |
| @@ -74,6 +74,7 @@ LIB_VTLS_HFILES = \ | |
| vtls/schannel.h \ | |
| vtls/sectransp.h \ | |
| vtls/vtls.h \ | |
| + | vtls/vtls_int.h \ |
| vtls/wolfssl.h \ | |
| vtls/x509asn1.h | |
| @@ -105,6 +106,7 @@ LIB_CFILES = \ | |
| base64.c \ | |
| bufref.c \ | |
| c-hyper.c \ | |
| + | cfilters.c \ |
| conncache.c \ | |
| connect.c \ | |
| content_encoding.c \ | |
| @@ -227,6 +229,7 @@ LIB_HFILES = \ | |
| asyn.h \ | |
| bufref.h \ | |
| c-hyper.h \ | |
| + | cfilters.h \ |
| conncache.h \ | |
| connect.h \ | |
| content_encoding.h \ | |
| @@ -0,0 +1,435 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | |
| + | #include "curl_setup.h" |
| + | |
| + | #include "urldata.h" |
| + | #include "strerror.h" |
| + | #include "cfilters.h" |
| + | #include "connect.h" |
| + | #include "url.h" /* for Curl_safefree() */ |
| + | #include "sendf.h" |
| + | #include "sockaddr.h" /* required for Curl_sockaddr_storage */ |
| + | #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" |
| + | #include "curl_memory.h" |
| + | #include "memdebug.h" |
| + | |
| + | #ifndef ARRAYSIZE |
| + | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) |
| + | #endif |
| + | |
| + | #ifdef DEBUGBUILD |
| + | static void cf_debug(struct Curl_easy *data, const char *fname, |
| + | struct connectdata *conn, int index, CURLcode result) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | char chain[128]; |
| + | size_t offset = 0, len; |
| + | |
| + | for(cf = conn->cfilter[index]; cf; cf = cf->next) { |
| + | len = strlen(cf->cft->name); |
| + | if(offset + len + 2 > sizeof(chain)) |
| + | break; |
| + | if(offset) { |
| + | chain[offset++] = '.'; |
| + | } |
| + | strcpy(chain + offset, cf->cft->name); |
| + | offset += len; |
| + | } |
| + | chain[offset] = 0; |
| + | infof(data, "%s(handle=%p, cfilter%d=[%s]) -> %d", |
| + | fname, data, index, chain, result); |
| + | } |
| + | |
| + | #endif |
| + | |
| + | void Curl_cf_def_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
| + | { |
| + | (void)cf; |
| + | (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); |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | void Curl_cfilter_destroy(struct Curl_easy *data, |
| + | struct connectdata *conn, int index) |
| + | { |
| + | struct Curl_cfilter *cfn, *cf = conn->cfilter[index]; |
| + | |
| + | if(cf) { |
| + | DEBUGF(infof(data, "Curl_cfilter_destroy(handle=%p, connection=%ld, " |
| + | "index=%d)", data, conn->connection_id, index)); |
| + | conn->cfilter[index] = NULL; |
| + | while(cf) { |
| + | cfn = cf->next; |
| + | cf->cft->destroy(cf, data); |
| + | free(cf); |
| + | cf = cfn; |
| + | } |
| + | } |
| + | } |
| + | |
| + | void Curl_cfilter_close(struct Curl_easy *data, |
| + | struct connectdata *conn, int index) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | |
| + | DEBUGASSERT(conn); |
| + | /* it is valid to call that without filters being present */ |
| + | cf = conn->cfilter[index]; |
| + | if(cf) { |
| + | DEBUGF(infof(data, "Curl_cfilter_close(handle=%p, index=%d)", |
| + | data, index)); |
| + | cf->cft->close(cf, data); |
| + | } |
| + | } |
| + | |
| + | ssize_t Curl_cfilter_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); |
| + | cf = data->conn->cfilter[num]; |
| + | while(cf && !cf->connected) { |
| + | cf = cf->next; |
| + | } |
| + | if(cf) { |
| + | nread = cf->cft->do_recv(cf, data, buf, len, code); |
| + | /* DEBUGF(infof(data, "Curl_cfilter_recv(handle=%p, index=%d)" |
| + | "-> %ld, err=%d", data, num, nread, *code));*/ |
| + | return nread; |
| + | } |
| + | failf(data, "no filter connected, conn=%ld, sockindex=%d", |
| + | data->conn->connection_id, num); |
| + | *code = CURLE_FAILED_INIT; |
| + | return -1; |
| + | } |
| + | |
| + | ssize_t Curl_cfilter_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); |
| + | cf = data->conn->cfilter[num]; |
| + | while(cf && !cf->connected) { |
| + | cf = cf->next; |
| + | } |
| + | if(cf) { |
| + | nwritten = cf->cft->do_send(cf, data, mem, len, code); |
| + | /* DEBUGF(infof(data, "Curl_cfilter_send(handle=%p, index=%d, len=%ld)" |
| + | " -> %ld, err=%d", data, num, len, nwritten, *code));*/ |
| + | return nwritten; |
| + | } |
| + | failf(data, "no filter connected, conn=%ld, sockindex=%d", |
| + | data->conn->connection_id, num); |
| + | *code = CURLE_FAILED_INIT; |
| + | return -1; |
| + | } |
| + | |
| + | CURLcode Curl_cfilter_create(struct Curl_cfilter **pcf, |
| + | struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | const struct Curl_cftype *cft, |
| + | void *ctx) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result = CURLE_OUT_OF_MEMORY; |
| + | |
| + | (void)data; |
| + | (void)conn; |
| + | DEBUGASSERT(cft); |
| + | cf = calloc(sizeof(*cf), 1); |
| + | if(!cf) |
| + | goto out; |
| + | |
| + | cf->cft = cft; |
| + | cf->conn = conn; |
| + | cf->sockindex = sockindex; |
| + | cf->ctx = ctx; |
| + | result = CURLE_OK; |
| + | out: |
| + | *pcf = cf; |
| + | return result; |
| + | } |
| + | |
| + | void Curl_cfilter_add(struct Curl_easy *data, struct connectdata *conn, |
| + | int index, struct Curl_cfilter *cf) |
| + | { |
| + | (void)data; |
| + | DEBUGF(infof(data, "Curl_cfilter_add(conn=%ld, index=%d, filter=%s)", |
| + | conn->connection_id, index, cf->cft->name)); |
| + | |
| + | cf->next = conn->cfilter[index]; |
| + | cf->conn = conn; |
| + | cf->sockindex = index; |
| + | conn->cfilter[index] = cf; |
| + | } |
| + | |
| + | CURLcode Curl_cfilter_setup(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | const struct Curl_dns_entry *remotehost, |
| + | int ssl_mode) |
| + | { |
| + | 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, "Curl_cfilter_setup(conn #%ld, index=%d)", |
| + | conn->connection_id, sockindex)); |
| + | result = Curl_cfilter_socket_set(data, conn, sockindex); |
| + | if(result) |
| + | goto out; |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | if(conn->bits.socksproxy) { |
| + | result = Curl_cfilter_socks_proxy_add(data, conn, sockindex); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | if(conn->bits.httpproxy) { |
| + | #ifdef USE_SSL |
| + | if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { |
| + | result = Curl_cfilter_ssl_proxy_add(data, conn, sockindex); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | #endif /* USE_SSL */ |
| + | |
| + | #if !defined(CURL_DISABLE_HTTP) |
| + | if(conn->bits.tunnel_proxy) { |
| + | result = Curl_cfilter_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_cfilter_ssl_add(data, conn, sockindex); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | #else |
| + | (void)ssl_mode; |
| + | #endif /* USE_SSL */ |
| + | } |
| + | DEBUGASSERT(conn->cfilter[sockindex]); |
| + | cf = data->conn->cfilter[sockindex]; |
| + | result = cf->cft->setup(cf, data, remotehost); |
| + | out: |
| + | DEBUGF(cf_debug(data, "Curl_cfilter_setup", conn, sockindex, result)); |
| + | return result; |
| + | } |
| + | |
| + | CURLcode Curl_cfilter_connect(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | bool blocking, bool *done) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | DEBUGASSERT(data); |
| + | |
| + | cf = conn->cfilter[sockindex]; |
| + | DEBUGASSERT(cf); |
| + | result = cf->cft->connect(cf, data, blocking, done); |
| + | |
| + | DEBUGF(infof(data, "Curl_cfilter_connect(handle=%p, index=%d, block=%d) " |
| + | "-> %d, done=%d", data, sockindex, blocking, result, *done)); |
| + | return result; |
| + | } |
| + | |
| + | bool Curl_cfilter_is_connected(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | |
| + | (void)data; |
| + | cf = conn->cfilter[sockindex]; |
| + | return cf && cf->connected; |
| + | } |
| + | |
| + | bool Curl_cfilter_data_pending(const struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | |
| + | DEBUGASSERT(data); |
| + | cf = conn->cfilter[sockindex]; |
| + | while(cf && !cf->connected) { |
| + | cf = cf->next; |
| + | } |
| + | if(cf) { |
| + | return cf->cft->has_data_pending(cf, data); |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| + | int Curl_cfilter_get_select_socks(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | curl_socket_t *socks) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | |
| + | DEBUGASSERT(data); |
| + | cf = conn->cfilter[sockindex]; |
| + | if(cf) { |
| + | return cf->cft->get_select_socks(cf, data, socks); |
| + | } |
| + | return GETSOCK_BLANK; |
| + | } |
| + | |
| + | void Curl_cfilter_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) { |
| + | DEBUGF(infof(data, "Curl_cfilter_attach(handle=%p, connection=%ld, " |
| + | "index=%d)", data, conn->connection_id, i)); |
| + | while(cf) { |
| + | cf->cft->attach_data(cf, data); |
| + | cf = cf->next; |
| + | } |
| + | } |
| + | } |
| + | } |
| + | |
| + | void Curl_cfilter_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) { |
| + | DEBUGF(infof(data, "Curl_cfilter_detach(handle=%p, connection=%ld, " |
| + | "index=%d)", data, conn->connection_id, i)); |
| + | while(cf) { |
| + | cf->cft->detach_data(cf, data); |
| + | cf = cf->next; |
| + | } |
| + | } |
| + | } |
| + | } |
| + | |
| @@ -0,0 +1,206 @@ | |
| + | #ifndef HEADER_CURL_CFILTERS_H |
| + | #define HEADER_CURL_CFILTERS_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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_cfilter; |
| + | struct Curl_easy; |
| + | |
| + | /* Destroy a filter instance. Implementations MUST NOT chain calls to cf->next. |
| + | */ |
| + | typedef void Curl_cf_destroy(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data); |
| + | |
| + | /* Setup the connection for `data`, using destination `remotehost`. |
| + | */ |
| + | typedef CURLcode Curl_cf_setup(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | const struct Curl_dns_entry *remotehost); |
| + | typedef void Curl_cf_close(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data); |
| + | |
| + | typedef CURLcode Curl_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool blocking, bool *done); |
| + | |
| + | /* Filters may return sockets and fdset flags they are waiting for. |
| + | * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets. |
| + | * @return read/write fdset for index in socks |
| + | * or GETSOCK_BLANK when nothing to wait on |
| + | */ |
| + | typedef int Curl_cf_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks); |
| + | |
| + | typedef bool Curl_cf_data_pending(struct Curl_cfilter *cf, |
| + | const struct Curl_easy *data); |
| + | |
| + | typedef ssize_t Curl_cf_send(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, /* transfer */ |
| + | const void *buf, /* data to write */ |
| + | size_t len, /* max amount to write */ |
| + | CURLcode *err); /* error to return */ |
| + | |
| + | typedef ssize_t Curl_cf_recv(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, /* transfer */ |
| + | char *buf, /* store data here */ |
| + | size_t len, /* max amount to read */ |
| + | CURLcode *err); /* error to return */ |
| + | |
| + | typedef void Curl_cf_attach_data(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data); |
| + | typedef void Curl_cf_detach_data(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. |
| + | */ |
| + | void Curl_cfilter_detach(struct connectdata *conn, struct Curl_easy *data); |
| + | |
| + | /* A connection filter type, e.g. specific implementation. */ |
| + | struct Curl_cftype { |
| + | const char *name; /* name of the filter type */ |
| + | Curl_cf_destroy *destroy; /* destroy resources held */ |
| + | Curl_cf_attach_data *attach_data; /* data is being handled here */ |
| + | Curl_cf_detach_data *detach_data; /* data is no longer handled here */ |
| + | Curl_cf_setup *setup; /* setup for a connection */ |
| + | Curl_cf_close *close; /* close conn */ |
| + | Curl_cf_connect *connect; /* establish connection */ |
| + | Curl_cf_get_select_socks *get_select_socks;/* sockets to select on */ |
| + | Curl_cf_data_pending *has_data_pending;/* conn has data pending */ |
| + | Curl_cf_send *do_send; /* send data */ |
| + | Curl_cf_recv *do_recv; /* receive data */ |
| + | }; |
| + | |
| + | /* A connection filter instance, e.g. registered at a connection */ |
| + | struct Curl_cfilter { |
| + | const struct Curl_cftype *cft; /* the type providing implementation */ |
| + | 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 */ |
| + | BIT(connected); /* != 0 iff this filter is connected */ |
| + | }; |
| + | |
| + | /* Default implementations for the type functions, implementing nop. */ |
| + | void Curl_cf_def_destroy(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data); |
| + | |
| + | /* 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, |
| + | bool blocking, bool *done); |
| + | int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks); |
| + | bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, |
| + | const struct Curl_easy *data); |
| + | 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_cfilter_create(struct Curl_cfilter **pcf, |
| + | struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | const struct Curl_cftype *cft, |
| + | void *ctx); |
| + | |
| + | void Curl_cfilter_destroy(struct Curl_easy *data, |
| + | struct connectdata *conn, int index); |
| + | |
| + | void Curl_cfilter_add(struct Curl_easy *data, |
| + | struct connectdata *conn, int index, |
| + | struct Curl_cfilter *cf); |
| + | |
| + | |
| + | #define CURL_CF_SSL_DEFAULT -1 |
| + | #define CURL_CF_SSL_DISABLE 0 |
| + | #define CURL_CF_SSL_ENABLE 1 |
| + | |
| + | CURLcode Curl_cfilter_setup(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | const struct Curl_dns_entry *remotehost, |
| + | int ssl_mode); |
| + | CURLcode Curl_cfilter_connect(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | bool blocking, bool *done); |
| + | bool Curl_cfilter_is_connected(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex); |
| + | |
| + | void Curl_cfilter_close(struct Curl_easy *data, |
| + | struct connectdata *conn, int index); |
| + | |
| + | bool Curl_cfilter_data_pending(const struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex); |
| + | |
| + | /** |
| + | * Get any select fd flags and the socket filters might be waiting for. |
| + | */ |
| + | int Curl_cfilter_get_select_socks(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | curl_socket_t *socks); |
| + | |
| + | /* Helper function to migrate conn->recv, conn->send callback to filters */ |
| + | ssize_t Curl_cfilter_recv(struct Curl_easy *data, int num, char *buf, |
| + | size_t len, CURLcode *code); |
| + | ssize_t Curl_cfilter_send(struct Curl_easy *data, int num, |
| + | const void *mem, 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. |
| + | */ |
| + | void Curl_cfilter_attach_data(struct connectdata *conn, |
| + | 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. |
| + | */ |
| + | void Curl_cfilter_detach_data(struct connectdata *conn, |
| + | struct Curl_easy *data); |
| + | |
| + | #endif /* HEADER_CURL_CFILTERS_H */ |
| @@ -64,6 +64,7 @@ | |
| #include "sendf.h" | |
| #include "if2ip.h" | |
| #include "strerror.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "select.h" | |
| #include "url.h" /* for Curl_safefree() */ | |
| @@ -79,7 +80,6 @@ | |
| #include "share.h" | |
| #include "version_win32.h" | |
| #include "quic.h" | |
| - | #include "socks.h" |
| /* The last 3 #include files should be in this order */ | |
| #include "curl_printf.h" | |
| @@ -237,10 +237,9 @@ timediff_t Curl_timeleft(struct Curl_easy *data, | |
| return timeout_ms; | |
| } | |
| - | static CURLcode bindlocal(struct Curl_easy *data, |
| + | static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, |
| curl_socket_t sockfd, int af, unsigned int scope) | |
| { | |
| - | struct connectdata *conn = data->conn; |
| 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 */ | |
| @@ -776,95 +775,25 @@ void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, | |
| Curl_persistconninfo(data, conn, local_ip, local_port); | |
| } | |
| - | /* After a TCP connection to the proxy has been verified, this function does |
| - | the next magic steps. If 'done' isn't set TRUE, it is not done yet and |
| - | must be called again. |
| - | |
| - | Note: this function's sub-functions call failf() |
| - | |
| - | */ |
| - | static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex, |
| - | bool *done) |
| - | { |
| - | CURLcode result = CURLE_OK; |
| - | #ifndef CURL_DISABLE_PROXY |
| - | CURLproxycode pxresult = CURLPX_OK; |
| - | struct connectdata *conn = data->conn; |
| - | if(conn->bits.socksproxy) { |
| - | /* for the secondary socket (FTP), use the "connect to host" |
| - | * but ignore the "connect to port" (use the secondary port) |
| - | */ |
| - | const char * const host = |
| - | conn->bits.httpproxy ? |
| - | conn->http_proxy.host.name : |
| - | conn->bits.conn_to_host ? |
| - | conn->conn_to_host.name : |
| - | sockindex == SECONDARYSOCKET ? |
| - | conn->secondaryhostname : conn->host.name; |
| - | const int port = |
| - | conn->bits.httpproxy ? (int)conn->http_proxy.port : |
| - | sockindex == SECONDARYSOCKET ? conn->secondary_port : |
| - | conn->bits.conn_to_port ? conn->conn_to_port : |
| - | conn->remote_port; |
| - | switch(conn->socks_proxy.proxytype) { |
| - | case CURLPROXY_SOCKS5: |
| - | case CURLPROXY_SOCKS5_HOSTNAME: |
| - | pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, |
| - | host, port, sockindex, data, done); |
| - | break; |
| - | |
| - | case CURLPROXY_SOCKS4: |
| - | case CURLPROXY_SOCKS4A: |
| - | pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, |
| - | data, done); |
| - | break; |
| - | |
| - | default: |
| - | failf(data, "unknown proxytype option given"); |
| - | result = CURLE_COULDNT_CONNECT; |
| - | } /* switch proxytype */ |
| - | if(pxresult) { |
| - | result = CURLE_PROXY; |
| - | data->info.pxcode = pxresult; |
| - | } |
| - | } |
| - | else |
| - | #else |
| - | (void)data; |
| - | (void)sockindex; |
| - | #endif /* CURL_DISABLE_PROXY */ |
| - | *done = TRUE; /* no SOCKS proxy, so consider us connected */ |
| - | |
| - | return result; |
| - | } |
| - | |
| /* | |
| - | * post_SOCKS() is called after a successful connect to the peer, which |
| - | * *could* be a SOCKS proxy |
| + | * post_connect() is called after a successful connect to the peer |
| */ | |
| - | static void post_SOCKS(struct Curl_easy *data, |
| + | static void post_connect(struct Curl_easy *data, |
| struct connectdata *conn, | |
| - | int sockindex, |
| - | bool *connected) |
| + | int sockindex) |
| { | |
| - | conn->bits.tcpconnect[sockindex] = TRUE; |
| - | |
| - | *connected = TRUE; |
| - | if(sockindex == FIRSTSOCKET) |
| - | Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ |
| Curl_updateconninfo(data, conn, conn->sock[sockindex]); | |
| Curl_verboseconnect(data, conn); | |
| data->info.numconnects++; /* to track the number of connections made */ | |
| } | |
| /* | |
| - | * Curl_is_connected() checks if the socket has connected. |
| + | * is_connected() checks if the socket has connected. |
| */ | |
| - | |
| - | CURLcode Curl_is_connected(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | int sockindex, |
| - | bool *connected) |
| + | static CURLcode is_connected(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | bool *connected) |
| { | |
| CURLcode result = CURLE_OK; | |
| timediff_t allow; | |
| @@ -877,22 +806,14 @@ CURLcode Curl_is_connected(struct Curl_easy *data, | |
| *connected = FALSE; /* a very negative world view is best */ | |
| - | if(conn->bits.tcpconnect[sockindex]) { |
| - | /* we are connected already! */ |
| - | *connected = TRUE; |
| - | return CURLE_OK; |
| - | } |
| - | |
| now = Curl_now(); | |
| - | if(SOCKS_STATE(conn->cnnct.state)) { |
| - | /* still doing SOCKS */ |
| - | result = connect_SOCKS(data, sockindex, connected); |
| - | if(!result && *connected) |
| - | post_SOCKS(data, conn, sockindex, connected); |
| - | return result; |
| - | } |
| - | |
| + | /* 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) | |
| @@ -906,7 +827,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data, | |
| conn->sock[sockindex] = conn->tempsock[i]; | |
| conn->ip_addr = conn->tempaddr[i]; | |
| conn->tempsock[i] = CURL_SOCKET_BAD; | |
| - | post_SOCKS(data, conn, sockindex, connected); |
| + | post_connect(data, conn, sockindex); |
| connkeep(conn, "HTTP/3 default"); | |
| if(conn->tempsock[other] != CURL_SOCKET_BAD) | |
| Curl_quic_disconnect(data, conn, other); | |
| @@ -968,14 +889,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data, | |
| conn->tempsock[other] = CURL_SOCKET_BAD; | |
| } | |
| - | /* see if we need to kick off any SOCKS proxy magic once we |
| - | connected */ |
| - | result = connect_SOCKS(data, sockindex, connected); |
| - | if(result || !*connected) |
| - | return result; |
| - | |
| - | post_SOCKS(data, conn, sockindex, connected); |
| - | |
| + | *connected = TRUE; |
| return CURLE_OK; | |
| } | |
| } | |
| @@ -1266,7 +1180,7 @@ static CURLcode singleipconnect(struct Curl_easy *data, | |
| || addr.family == AF_INET6 | |
| #endif | |
| ) { | |
| - | result = bindlocal(data, sockfd, addr.family, |
| + | 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 */ | |
| @@ -1529,7 +1443,7 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, | |
| bool Curl_connalive(struct connectdata *conn) | |
| { | |
| /* First determine if ssl */ | |
| - | if(conn->ssl[FIRSTSOCKET].use) { |
| + | if(Curl_ssl_use(conn, FIRSTSOCKET)) { |
| /* use the SSL context */ | |
| if(!Curl_ssl_check_cxn(conn)) | |
| return false; /* FIN received */ | |
| @@ -1558,6 +1472,8 @@ bool Curl_connalive(struct connectdata *conn) | |
| int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, | |
| curl_socket_t sock) | |
| { | |
| + | DEBUGF(infof(data, "Curl_closesocket(conn #%ld, sock=%d), fclosesocket=%d", |
| + | conn->connection_id, (int)sock, conn->fclosesocket)); |
| 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 | |
| @@ -1720,15 +1636,313 @@ void Curl_conncontrol(struct connectdata *conn, | |
| } | |
| /* Data received can be cached at various levels, so check them all here. */ | |
| - | bool Curl_conn_data_pending(struct connectdata *conn, int sockindex) |
| + | bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) |
| { | |
| - | int readable; |
| - | DEBUGASSERT(conn); |
| + | return Curl_recv_has_postponed_data(data->conn, sockindex) |
| + | ||Curl_cfilter_data_pending(data, data->conn, sockindex); |
| + | } |
| + | |
| + | 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; |
| + | }; |
| + | |
| + | static int socket_cf_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks) |
| + | { |
| + | struct connectdata *conn = cf->conn; |
| + | int i, s, rc = GETSOCK_BLANK; |
| + | |
| + | (void)data; |
| + | if(cf->connected) { |
| + | return rc; |
| + | } |
| + | |
| + | for(i = s = 0; i<2; i++) { |
| + | if(conn->tempsock[i] != CURL_SOCKET_BAD) { |
| + | socks[s] = conn->tempsock[i]; |
| + | rc |= GETSOCK_WRITESOCK(s); |
| + | #ifdef ENABLE_QUIC |
| + | if(conn->transport == TRNSPRT_QUIC) |
| + | /* when connecting QUIC, we want to read the socket too */ |
| + | rc |= GETSOCK_READSOCK(s); |
| + | #endif |
| + | s++; |
| + | } |
| + | } |
| - | if(Curl_ssl_data_pending(conn, sockindex) || |
| - | Curl_recv_has_postponed_data(conn, sockindex)) |
| - | return true; |
| + | return rc; |
| + | } |
| + | |
| + | static CURLcode socket_cf_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; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| - | readable = SOCKET_READABLE(conn->sock[sockindex], 0); |
| + | (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; |
| + | DEBUGF(infof(data, "socket_cf_connect(handle=%p, index=%d) -> " |
| + | "connecthost() -> %d, done=%d", |
| + | data, sockindex, result, *done)); |
| + | 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_ssl_use(conn, 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; |
| + | } |
| + | DEBUGF(infof(data, "socket_cf_connect(handle=%p, index=%d) -> " |
| + | "is_connected() -> %d, done=%d", |
| + | data, sockindex, result, *done)); |
| + | break; |
| + | case SCFST_DONE: |
| + | *done = TRUE; |
| + | DEBUGF(infof(data, "socket_cf_connect(handle=%p, index=%d) -> " |
| + | "already connected-> %d, done=%d", |
| + | data, sockindex, result, *done)); |
| + | break; |
| + | } |
| + | 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; |
| + | bool done; |
| + | |
| + | DEBUGASSERT(ctx); |
| + | if(ctx->remotehost != remotehost) { |
| + | if(ctx->remotehost) { |
| + | /* switching dns entry? TODO: reset? */ |
| + | } |
| + | ctx->remotehost = remotehost; |
| + | } |
| + | /* we start connecting right on setup */ |
| + | DEBUGF(infof(data, "socket_cf_setup(handle=%p, remotehost=%s)", |
| + | data, cf->conn->hostname_resolve)); |
| + | return socket_cf_connect(cf, data, FALSE, &done); |
| + | } |
| + | |
| + | 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(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) { |
| + | Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]); |
| + | cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD; |
| + | } |
| + | cf->connected = FALSE; |
| + | ctx->state = SCFST_INIT; |
| + | } |
| + | |
| + | static bool socket_cf_data_pending(struct Curl_cfilter *cf, |
| + | const struct Curl_easy *data) |
| + | { |
| + | int readable; |
| + | (void)data; |
| + | DEBUGASSERT(cf); |
| + | |
| + | readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0); |
| return (readable > 0 && (readable & CURL_CSELECT_IN)); | |
| } | |
| + | |
| + | static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, |
| + | const void *buf, size_t len, CURLcode *err) |
| + | { |
| + | ssize_t nwritten; |
| + | nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err); |
| + | /* DEBUGF(infof(data, "socket_cf_send(handle=%p, index=%d, len=%ld)" |
| + | "-> %ld, code=%d", data, cf->sockindex, len, nwritten, *err));*/ |
| + | return nwritten; |
| + | } |
| + | |
| + | 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); |
| + | /* DEBUGF(infof(data, "socket_cf_recv(handle=%p, index=%d) -> %ld", |
| + | data, cf->sockindex, nread));*/ |
| + | return nread; |
| + | } |
| + | |
| + | static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
| + | { |
| + | struct socket_cf_ctx *state = cf->ctx; |
| + | |
| + | (void)data; |
| + | DEBUGF(infof(data, "socket_cf_destroy(conn #%ld, index=%d", |
| + | cf->conn->connection_id, cf->sockindex)); |
| + | if(cf->connected) { |
| + | socket_cf_close(cf, data); |
| + | } |
| + | /* release any resources held in state */ |
| + | Curl_safefree(state); |
| + | } |
| + | |
| + | static const struct Curl_cftype cft_socket = { |
| + | "SOCKET", |
| + | socket_cf_destroy, |
| + | Curl_cf_def_attach_data, |
| + | Curl_cf_def_detach_data, |
| + | socket_cf_setup, |
| + | socket_cf_close, |
| + | socket_cf_connect, |
| + | socket_cf_get_select_socks, |
| + | socket_cf_data_pending, |
| + | socket_cf_send, |
| + | socket_cf_recv, |
| + | }; |
| + | |
| + | CURLcode Curl_cfilter_socket_set(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | CURLcode result; |
| + | struct Curl_cfilter *cf = NULL; |
| + | struct socket_cf_ctx *scf_ctx = NULL; |
| + | |
| + | /* Need to be first */ |
| + | DEBUGASSERT(!conn->cfilter[sockindex]); |
| + | scf_ctx = calloc(sizeof(*scf_ctx), 1); |
| + | if(!scf_ctx) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_socket, scf_ctx); |
| + | if(result) |
| + | goto out; |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | |
| + | out: |
| + | if(result) { |
| + | Curl_safefree(cf); |
| + | Curl_safefree(scf_ctx); |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | static CURLcode socket_accept_cf_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; |
| + | } |
| + | |
| + | static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | const struct Curl_dns_entry *remotehost) |
| + | { |
| + | /* 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; |
| + | } |
| + | |
| + | static const struct Curl_cftype cft_socket_accept = { |
| + | "SOCKET-ACCEPT", |
| + | socket_cf_destroy, |
| + | Curl_cf_def_attach_data, |
| + | Curl_cf_def_detach_data, |
| + | socket_accept_cf_setup, |
| + | socket_cf_close, |
| + | socket_accept_cf_connect, |
| + | Curl_cf_def_get_select_socks, |
| + | socket_cf_data_pending, |
| + | socket_cf_send, |
| + | socket_cf_recv, |
| + | }; |
| + | |
| + | CURLcode Curl_cfilter_socket_accepted_set(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, curl_socket_t *s) |
| + | { |
| + | CURLcode result; |
| + | struct Curl_cfilter *cf = NULL; |
| + | struct socket_cf_ctx *scf_ctx = NULL; |
| + | |
| + | 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_cfilter_destroy(data, conn, sockindex); |
| + | scf_ctx = calloc(sizeof(*scf_ctx), 1); |
| + | if(!scf_ctx) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_socket_accept, scf_ctx); |
| + | if(result) |
| + | goto out; |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | } |
| + | |
| + | /* 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; |
| + | |
| + | out: |
| + | if(result) { |
| + | Curl_safefree(cf); |
| + | Curl_safefree(scf_ctx); |
| + | } |
| + | return result; |
| + | } |
| @@ -29,11 +29,6 @@ | |
| #include "sockaddr.h" | |
| #include "timeval.h" | |
| - | CURLcode Curl_is_connected(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | int sockindex, |
| - | bool *connected); |
| - | |
| CURLcode Curl_connecthost(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| const struct Curl_dns_entry *host); | |
| @@ -153,6 +148,14 @@ void Curl_conncontrol(struct connectdata *conn, | |
| #define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP) | |
| #endif | |
| - | bool Curl_conn_data_pending(struct connectdata *conn, int sockindex); |
| + | bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex); |
| + | |
| + | CURLcode Curl_cfilter_socket_set(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | |
| + | CURLcode Curl_cfilter_socket_accepted_set(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, curl_socket_t *s); |
| #endif /* HEADER_CURL_CONNECT_H */ | |
| @@ -150,9 +150,19 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) | |
| char *actual_path; | |
| #endif | |
| size_t real_path_len; | |
| + | CURLcode result; |
| + | |
| + | if(file->path) { |
| + | /* already connected. |
| + | * the handler->connect_it() is normally only called once, but |
| + | * FILE does a special check on setting up the connection which |
| + | * calls this explicitly. */ |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| - | CURLcode result = Curl_urldecode(data->state.up.path, 0, &real_path, |
| - | &real_path_len, REJECT_ZERO); |
| + | result = Curl_urldecode(data->state.up.path, 0, &real_path, |
| + | &real_path_len, REJECT_ZERO); |
| if(result) | |
| return result; | |
| @@ -226,6 +236,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) | |
| file->path = real_path; | |
| #endif | |
| #endif | |
| + | Curl_safefree(file->freepath); |
| file->freepath = real_path; /* free this when done */ | |
| file->fd = fd; | |
| @@ -65,6 +65,7 @@ | |
| #include "strtoofft.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "strerror.h" | |
| #include "inet_ntop.h" | |
| @@ -218,14 +219,8 @@ const struct Curl_handler Curl_handler_ftps = { | |
| static void close_secondarysocket(struct Curl_easy *data, | |
| struct connectdata *conn) | |
| { | |
| - | if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) { |
| - | Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); |
| - | conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; |
| - | } |
| - | conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; |
| - | #ifndef CURL_DISABLE_PROXY |
| - | conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE; |
| - | #endif |
| + | Curl_cfilter_close(data, conn, SECONDARYSOCKET); |
| + | Curl_cfilter_destroy(data, conn, SECONDARYSOCKET); |
| } | |
| /* | |
| @@ -277,13 +272,13 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) | |
| struct sockaddr_in add; | |
| #endif | |
| curl_socklen_t size = (curl_socklen_t) sizeof(add); | |
| + | CURLcode result; |
| if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { | |
| size = sizeof(add); | |
| s = accept(sock, (struct sockaddr *) &add, &size); | |
| } | |
| - | Curl_closesocket(data, conn, sock); /* close the first socket */ |
| if(CURL_SOCKET_BAD == s) { | |
| failf(data, "Error accept()ing server connect"); | |
| @@ -294,9 +289,11 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) | |
| not needing DO_MORE anymore */ | |
| conn->bits.do_more = FALSE; | |
| - | conn->sock[SECONDARYSOCKET] = s; |
| (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ | |
| - | conn->bits.sock_accepted = TRUE; |
| + | /* Replace any filter on SECONDARY with one listeing on this socket */ |
| + | result = Curl_cfilter_socket_accepted_set(data, conn, SECONDARYSOCKET, &s); |
| + | if(result) |
| + | return result; |
| if(data->set.fsockopt) { | |
| int error = 0; | |
| @@ -440,15 +437,13 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) | |
| { | |
| CURLcode result = CURLE_OK; | |
| struct connectdata *conn = data->conn; | |
| + | bool connected; |
| - | if(conn->bits.ftp_use_data_ssl) { |
| - | /* since we only have a plaintext TCP connection here, we must now |
| - | * do the TLS stuff */ |
| - | infof(data, "Doing the SSL/TLS handshake on the data stream"); |
| - | result = Curl_ssl_connect(data, conn, SECONDARYSOCKET); |
| - | if(result) |
| - | return result; |
| - | } |
| + | DEBUGF(infof(data, "ftp InitiateTransfer()")); |
| + | result = Curl_cfilter_connect(data, conn, SECONDARYSOCKET, |
| + | TRUE, &connected); |
| + | if(result || !connected) |
| + | return result; |
| if(conn->proto.ftpc.state_saved == FTP_STOR) { | |
| /* When we know we're uploading a specified file, we can get the file | |
| @@ -496,22 +491,23 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) | |
| if(timeout_ms < 0) { | |
| /* if a timeout was already reached, bail out */ | |
| failf(data, "Accept timeout occurred while waiting server connect"); | |
| - | return CURLE_FTP_ACCEPT_TIMEOUT; |
| + | result = CURLE_FTP_ACCEPT_TIMEOUT; |
| + | goto out; |
| } | |
| /* see if the connection request is already here */ | |
| result = ReceivedServerConnect(data, connected); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(*connected) { | |
| result = AcceptServerConnect(data); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| result = InitiateTransfer(data); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| } | |
| else { | |
| /* Add timeout to multi handle and break out of the loop */ | |
| @@ -520,6 +516,8 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) | |
| EXPIRE_FTP_ACCEPT); | |
| } | |
| + | out: |
| + | DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result)); |
| return result; | |
| } | |
| @@ -671,7 +669,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, | |
| * wait for more data anyway. | |
| */ | |
| } | |
| - | else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) { |
| + | else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { |
| switch(SOCKET_READABLE(sockfd, interval_ms)) { | |
| case -1: /* select() error, stop reading */ | |
| failf(data, "FTP response aborted due to select/poll error: %d", | |
| @@ -820,8 +818,10 @@ static int ftp_domore_getsock(struct Curl_easy *data, | |
| * handle ordinary commands. | |
| */ | |
| - | if(SOCKS_STATE(conn->cnnct.state)) |
| - | return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); |
| + | DEBUGF(infof(data, "ftp_domore_getsock()")); |
| + | if(conn->cfilter[SECONDARYSOCKET] |
| + | && !Curl_cfilter_is_connected(data, conn, SECONDARYSOCKET)) |
| + | return Curl_cfilter_get_select_socks(data, conn, SECONDARYSOCKET, socks); |
| if(FTP_STOP == ftpc->state) { | |
| int bits = GETSOCK_READSOCK(0); | |
| @@ -916,7 +916,7 @@ typedef enum { | |
| static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| ftpport fcmd) /* start with this */ | |
| { | |
| - | CURLcode result = CURLE_OK; |
| + | CURLcode result = CURLE_FTP_PORT_FAILED; |
| struct connectdata *conn = data->conn; | |
| struct ftp_conn *ftpc = &conn->proto.ftpc; | |
| curl_socket_t portsock = CURL_SOCKET_BAD; | |
| @@ -965,8 +965,10 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| char *port_sep = NULL; | |
| addr = calloc(addrlen + 1, 1); | |
| - | if(!addr) |
| - | return CURLE_OUT_OF_MEMORY; |
| + | if(!addr) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| #ifdef ENABLE_IPV6 | |
| if(*string_ftpport == '[') { | |
| @@ -1026,7 +1028,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(port_min > port_max) | |
| port_min = port_max = 0; | |
| - | |
| if(*addr != '\0') { | |
| /* attempt to get the address of the given interface name */ | |
| switch(Curl_if2ip(conn->ip_addr->ai_family, | |
| @@ -1040,7 +1041,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| host = addr; | |
| break; | |
| case IF2IP_AF_NOT_SUPPORTED: | |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| case IF2IP_FOUND: | |
| host = hbuf; /* use the hbuf for host name */ | |
| } | |
| @@ -1058,8 +1059,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { | |
| failf(data, "getsockname() failed: %s", | |
| Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| - | free(addr); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| switch(sa->sa_family) { | |
| #ifdef ENABLE_IPV6 | |
| @@ -1071,8 +1071,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); | |
| break; | |
| } | |
| - | if(!r) |
| - | return CURLE_FTP_PORT_FAILED; |
| + | if(!r) { |
| + | goto out; |
| + | } |
| host = hbuf; /* use this host name */ | |
| possibly_non_local = FALSE; /* we know it is local now */ | |
| } | |
| @@ -1092,11 +1093,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(!res) { | |
| failf(data, "failed to resolve the address provided to PORT: %s", host); | |
| - | free(addr); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| - | free(addr); |
| host = NULL; | |
| /* step 2, create a socket for the requested address */ | |
| @@ -1104,8 +1103,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) { | |
| - | result = Curl_socket(data, ai, NULL, &portsock); |
| - | if(result) { |
| + | if(Curl_socket(data, ai, NULL, &portsock)) { |
| error = SOCKERRNO; | |
| continue; | |
| } | |
| @@ -1114,8 +1112,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(!ai) { | |
| failf(data, "socket failure: %s", | |
| Curl_strerror(error, buffer, sizeof(buffer))); | |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| + | DEBUGF(infof(data, "ftp_state_use_port(), opened socket")); |
| /* step 3, bind to a suitable local address */ | |
| @@ -1144,8 +1143,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { | |
| failf(data, "getsockname() failed: %s", | |
| Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| port = port_min; | |
| possibly_non_local = FALSE; /* don't try this again */ | |
| @@ -1154,8 +1152,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(error != EADDRINUSE && error != EACCES) { | |
| failf(data, "bind(port=%hu) failed: %s", port, | |
| Curl_strerror(error, buffer, sizeof(buffer))); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| } | |
| else | |
| @@ -1167,8 +1164,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| /* maybe all ports were in use already */ | |
| if(port > port_max) { | |
| failf(data, "bind() failed, we ran out of ports"); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| /* get the name again after the bind() so that we can extract the | |
| @@ -1177,18 +1173,18 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(getsockname(portsock, sa, &sslen)) { | |
| failf(data, "getsockname() failed: %s", | |
| Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| + | DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port)); |
| /* step 4, listen on the socket */ | |
| if(listen(portsock, 1)) { | |
| failf(data, "socket failure: %s", | |
| Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | return CURLE_FTP_PORT_FAILED; |
| + | goto out; |
| } | |
| + | DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port)); |
| /* step 5, send the proper FTP command */ | |
| @@ -1241,12 +1237,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(result) { | |
| failf(data, "Failure sending EPRT command: %s", | |
| curl_easy_strerror(result)); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | /* don't retry using PORT */ |
| - | ftpc->count1 = PORT; |
| - | /* bail out */ |
| - | state(data, FTP_STOP); |
| - | return result; |
| + | goto out; |
| } | |
| break; | |
| } | |
| @@ -1272,10 +1263,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| if(result) { | |
| failf(data, "Failure sending PORT command: %s", | |
| curl_easy_strerror(result)); | |
| - | Curl_closesocket(data, conn, portsock); |
| - | /* bail out */ |
| - | state(data, FTP_STOP); |
| - | return result; |
| + | goto out; |
| } | |
| break; | |
| } | |
| @@ -1284,23 +1272,21 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, | |
| /* store which command was sent */ | |
| ftpc->count1 = fcmd; | |
| - | close_secondarysocket(data, conn); |
| - | |
| - | /* we set the secondary socket variable to this for now, it is only so that |
| - | the cleanup function will close it in case we fail before the true |
| - | secondary stuff is made */ |
| - | conn->sock[SECONDARYSOCKET] = portsock; |
| - | |
| - | /* this tcpconnect assignment below is a hackish work-around to make the |
| - | multi interface with active FTP work - as it will not wait for a |
| - | (passive) connect in Curl_is_connected(). |
| - | |
| - | The *proper* fix is to make sure that the active connection from the |
| - | server is done in a non-blocking way. Currently, it is still BLOCKING. |
| - | */ |
| - | conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE; |
| - | |
| + | /* Replace any filter on SECONDARY with one listeing on this socket */ |
| + | result = Curl_cfilter_socket_accepted_set(data, conn, SECONDARYSOCKET, |
| + | &portsock); |
| + | if(result) |
| + | goto out; |
| + | portsock = CURL_SOCKET_BAD; /* now held in filter */ |
| state(data, FTP_PORT); | |
| + | |
| + | out: |
| + | if(result) { |
| + | state(data, FTP_STOP); |
| + | } |
| + | if(portsock != CURL_SOCKET_BAD) |
| + | Curl_closesocket(data, conn, portsock); |
| + | free(addr); |
| return result; | |
| } | |
| @@ -1803,6 +1789,8 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, | |
| infof(data, "Failed EPSV attempt. Disabling EPSV"); | |
| /* disable it for next transfer */ | |
| conn->bits.ftp_use_epsv = FALSE; | |
| + | Curl_cfilter_close(data, conn, SECONDARYSOCKET); |
| + | Curl_cfilter_destroy(data, conn, SECONDARYSOCKET); |
| data->state.errorbuf = FALSE; /* allow error message to get | |
| rewritten */ | |
| result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV"); | |
| @@ -1992,8 +1980,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| } | |
| } | |
| - | conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; |
| - | result = Curl_connecthost(data, conn, addr); |
| + | result = Curl_cfilter_setup(data, conn, SECONDARYSOCKET, addr, |
| + | conn->bits.ftp_use_data_ssl? |
| + | CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); |
| if(result) { | |
| Curl_resolv_unlock(data, addr); /* we're done using this address */ | |
| @@ -2209,6 +2198,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, | |
| struct connectdata *conn = data->conn; | |
| struct ftp_conn *ftpc = &conn->proto.ftpc; | |
| + | DEBUGF(infof(data, "ftp_state_retr()")); |
| if(data->set.max_filesize && (filesize > data->set.max_filesize)) { | |
| failf(data, "Maximum file size exceeded"); | |
| return CURLE_FILESIZE_EXCEEDED; | |
| @@ -2755,8 +2745,9 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, | |
| */ | |
| if((ftpcode == 234) || (ftpcode == 334)) { | |
| - | /* Curl_ssl_connect is BLOCKING */ |
| - | result = Curl_ssl_connect(data, conn, FIRSTSOCKET); |
| + | /* this was BLOCKING, keep it so for now */ |
| + | bool done; |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, TRUE, &done); |
| if(!result) { | |
| conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ | |
| conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ | |
| @@ -3166,7 +3157,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, | |
| if(conn->handler->flags & PROTOPT_SSL) { | |
| /* BLOCKING */ | |
| - | result = Curl_ssl_connect(data, conn, FIRSTSOCKET); |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, TRUE, done); |
| if(result) | |
| return result; | |
| conn->bits.ftp_use_control_ssl = TRUE; | |
| @@ -3309,14 +3300,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, | |
| } | |
| } | |
| - | if(conn->ssl[SECONDARYSOCKET].use) { |
| - | /* The secondary socket is using SSL so we must close down that part |
| - | first before we close the socket for real */ |
| - | Curl_ssl_close(data, conn, SECONDARYSOCKET); |
| - | |
| - | /* Note that we keep "use" set to TRUE since that (next) connection is |
| - | still requested to use SSL */ |
| - | } |
| close_secondarysocket(data, conn); | |
| } | |
| @@ -3571,22 +3554,10 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) | |
| struct FTP *ftp = NULL; | |
| /* if the second connection isn't done yet, wait for it */ | |
| - | if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { |
| - | if(Curl_connect_ongoing(conn)) { |
| - | /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port |
| - | aren't used so we blank their arguments. */ |
| - | result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0); |
| - | |
| - | return result; |
| - | } |
| - | |
| - | result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected); |
| - | |
| - | /* Ready to do more? */ |
| - | if(connected) { |
| - | DEBUGF(infof(data, "DO-MORE connected phase starts")); |
| - | } |
| - | else { |
| + | if(conn->cfilter[SECONDARYSOCKET]) { |
| + | result = Curl_cfilter_connect(data, conn, SECONDARYSOCKET, |
| + | FALSE, &connected); |
| + | if(result || !connected) { |
| if(result && (ftpc->count1 == 0)) { | |
| *completep = -1; /* go back to DOING please */ | |
| /* this is a EPSV connect failing, try PASV instead */ | |
| @@ -3596,19 +3567,6 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) | |
| } | |
| } | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | result = Curl_proxy_connect(data, SECONDARYSOCKET); |
| - | if(result) |
| - | return result; |
| - | |
| - | if(CONNECT_SECONDARYSOCKET_PROXY_SSL()) |
| - | return result; |
| - | |
| - | if(conn->bits.tunnel_proxy && conn->bits.httpproxy && |
| - | Curl_connect_ongoing(conn)) |
| - | return result; |
| - | #endif |
| - | |
| /* Curl_proxy_connect might have moved the protocol state */ | |
| ftp = data->req.p.ftp; | |
| @@ -3738,7 +3696,6 @@ CURLcode ftp_perform(struct Curl_easy *data, | |
| { | |
| /* this is FTP and no proxy */ | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -3758,7 +3715,7 @@ CURLcode ftp_perform(struct Curl_easy *data, | |
| /* run the state-machine */ | |
| result = ftp_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, SECONDARYSOCKET); |
| infof(data, "ftp_perform ends with SECONDARY: %d", *connected); | |
| @@ -4374,6 +4331,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, | |
| { | |
| char *type; | |
| struct FTP *ftp; | |
| + | CURLcode result = CURLE_OK; |
| data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); | |
| if(!ftp) | |
| @@ -4415,7 +4373,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, | |
| ftp->downloadsize = 0; | |
| conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ | |
| - | return CURLE_OK; |
| + | return result; |
| } | |
| #endif /* CURL_DISABLE_FTP */ | |
| @@ -533,13 +533,7 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, | |
| #ifdef USE_SSL | |
| if(conn && tsi->backend != CURLSSLBACKEND_NONE) { | |
| - | unsigned int i; |
| - | for(i = 0; i < (sizeof(conn->ssl) / sizeof(conn->ssl[0])); ++i) { |
| - | if(conn->ssl[i].use) { |
| - | tsi->internals = Curl_ssl->get_internals(&conn->ssl[i], info); |
| - | break; |
| - | } |
| - | } |
| + | tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0); |
| } | |
| #endif | |
| } | |
| @@ -30,6 +30,7 @@ | |
| #include <curl/curl.h> | |
| #include "transfer.h" | |
| #include "sendf.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "progress.h" | |
| #include "gopher.h" | |
| @@ -117,7 +118,9 @@ static CURLcode gopher_connect(struct Curl_easy *data, bool *done) | |
| static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) | |
| { | |
| struct connectdata *conn = data->conn; | |
| - | CURLcode result = Curl_ssl_connect(data, conn, FIRSTSOCKET); |
| + | CURLcode result; |
| + | |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, TRUE, done); |
| if(result) | |
| connclose(conn, "Failed TLS connection"); | |
| *done = TRUE; | |
| @@ -80,6 +80,7 @@ | |
| #include "http_proxy.h" | |
| #include "warnless.h" | |
| #include "http2.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "strdup.h" | |
| #include "altsvc.h" | |
| @@ -105,14 +106,6 @@ static bool http_should_fail(struct Curl_easy *data); | |
| static CURLcode add_haproxy_protocol_header(struct Curl_easy *data); | |
| #endif | |
| - | #ifdef USE_SSL |
| - | static CURLcode https_connecting(struct Curl_easy *data, bool *done); |
| - | static int https_getsock(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | curl_socket_t *socks); |
| - | #else |
| - | #define https_connecting(x,y) CURLE_COULDNT_CONNECT |
| - | #endif |
| static CURLcode http_setup_conn(struct Curl_easy *data, | |
| struct connectdata *conn); | |
| #ifdef USE_WEBSOCKETS | |
| @@ -184,9 +177,9 @@ const struct Curl_handler Curl_handler_https = { | |
| Curl_http_done, /* done */ | |
| ZERO_NULL, /* do_more */ | |
| Curl_http_connect, /* connect_it */ | |
| - | https_connecting, /* connecting */ |
| + | NULL, /* connecting */ |
| ZERO_NULL, /* doing */ | |
| - | https_getsock, /* proto_getsock */ |
| + | NULL, /* proto_getsock */ |
| http_getsock_do, /* doing_getsock */ | |
| ZERO_NULL, /* domore_getsock */ | |
| ZERO_NULL, /* perform_getsock */ | |
| @@ -209,9 +202,9 @@ const struct Curl_handler Curl_handler_wss = { | |
| Curl_http_done, /* done */ | |
| ZERO_NULL, /* do_more */ | |
| Curl_http_connect, /* connect_it */ | |
| - | https_connecting, /* connecting */ |
| + | NULL, /* connecting */ |
| ZERO_NULL, /* doing */ | |
| - | https_getsock, /* proto_getsock */ |
| + | NULL, /* proto_getsock */ |
| http_getsock_do, /* doing_getsock */ | |
| ZERO_NULL, /* domore_getsock */ | |
| ZERO_NULL, /* perform_getsock */ | |
| @@ -1555,23 +1548,11 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) | |
| function to make the re-use checks properly be able to check this bit. */ | |
| connkeep(conn, "HTTP default"); | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | /* the CONNECT procedure might not have been completed */ |
| - | result = Curl_proxy_connect(data, FIRSTSOCKET); |
| - | if(result) |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, FALSE, done); |
| + | if(result || !*done) |
| return result; | |
| - | if(conn->bits.proxy_connect_closed) |
| - | /* this is not an error, just part of the connection negotiation */ |
| - | return CURLE_OK; |
| - | |
| - | if(CONNECT_FIRSTSOCKET_PROXY_SSL()) |
| - | return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */ |
| - | |
| - | if(Curl_connect_ongoing(conn)) |
| - | /* nothing else to do except wait right now - we're not done here. */ |
| - | return CURLE_OK; |
| - | |
| + | #ifndef CURL_DISABLE_PROXY |
| if(data->set.haproxyprotocol && !data->state.is_haproxy_hdr_sent) { | |
| /* add HAProxy PROXY protocol header */ | |
| result = add_haproxy_protocol_header(data); | |
| @@ -1583,15 +1564,6 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) | |
| } | |
| #endif | |
| - | if(conn->given->flags & PROTOPT_SSL) { |
| - | /* perform SSL initialization */ |
| - | result = https_connecting(data, done); |
| - | if(result) |
| - | return result; |
| - | } |
| - | else |
| - | *done = TRUE; |
| - | |
| return CURLE_OK; | |
| } | |
| @@ -1644,39 +1616,6 @@ static CURLcode add_haproxy_protocol_header(struct Curl_easy *data) | |
| } | |
| #endif | |
| - | #ifdef USE_SSL |
| - | static CURLcode https_connecting(struct Curl_easy *data, bool *done) |
| - | { |
| - | CURLcode result; |
| - | struct connectdata *conn = data->conn; |
| - | DEBUGASSERT((data) && (data->conn->handler->flags & PROTOPT_SSL)); |
| - | |
| - | #ifdef ENABLE_QUIC |
| - | if(conn->transport == TRNSPRT_QUIC) { |
| - | *done = TRUE; |
| - | return CURLE_OK; |
| - | } |
| - | #endif |
| - | |
| - | /* perform SSL initialization for this socket */ |
| - | result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done); |
| - | if(result) |
| - | connclose(conn, "Failed HTTPS connection"); |
| - | |
| - | return result; |
| - | } |
| - | |
| - | static int https_getsock(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | curl_socket_t *socks) |
| - | { |
| - | (void)data; |
| - | if(conn->handler->flags & PROTOPT_SSL) |
| - | return Curl_ssl->getsock(conn, socks); |
| - | return GETSOCK_BLANK; |
| - | } |
| - | #endif /* USE_SSL */ |
| - | |
| /* | |
| * Curl_http_done() gets called after a single HTTP request has been | |
| * performed. | |
| @@ -37,6 +37,7 @@ | |
| #include "url.h" | |
| #include "select.h" | |
| #include "progress.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "curlx.h" | |
| #include "vtls/vtls.h" | |
| @@ -48,179 +49,197 @@ | |
| #include "curl_memory.h" | |
| #include "memdebug.h" | |
| - | /* |
| - | * Perform SSL initialization for HTTPS proxy. Sets |
| - | * proxy_ssl_connected connection bit when complete. Can be |
| - | * called multiple times. |
| - | */ |
| - | static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex) |
| + | typedef enum { |
| + | TUNNEL_INIT, /* init/default/no tunnel state */ |
| + | TUNNEL_CONNECT, /* CONNECT request is being send */ |
| + | TUNNEL_RECEIVE, /* CONNECT answer is being received */ |
| + | TUNNEL_RESPONSE, /* CONNECT response received completely */ |
| + | TUNNEL_ESTABLISHED, |
| + | TUNNEL_FAILED |
| + | } tunnel_state; |
| + | |
| + | /* struct for HTTP CONNECT tunneling */ |
| + | struct tunnel_state { |
| + | int sockindex; |
| + | const char *hostname; |
| + | int remote_port; |
| + | struct HTTP http_proxy; |
| + | struct HTTP *prot_save; |
| + | struct dynbuf rcvbuf; |
| + | struct dynbuf req; |
| + | size_t nsend; |
| + | size_t headerlines; |
| + | enum keeponval { |
| + | KEEPON_DONE, |
| + | KEEPON_CONNECT, |
| + | KEEPON_IGNORE |
| + | } keepon; |
| + | curl_off_t cl; /* size of content to read and ignore */ |
| + | tunnel_state tunnel_state; |
| + | BIT(chunked_encoding); |
| + | BIT(close_connection); |
| + | }; |
| + | |
| + | |
| + | static bool tunnel_is_established(struct tunnel_state *ts) |
| { | |
| - | #ifdef USE_SSL |
| - | struct connectdata *conn = data->conn; |
| - | CURLcode result = CURLE_OK; |
| - | DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS); |
| - | if(!conn->bits.proxy_ssl_connected[sockindex]) { |
| - | /* perform SSL initialization for this socket */ |
| - | result = |
| - | Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex, |
| - | &conn->bits.proxy_ssl_connected[sockindex]); |
| - | if(result) |
| - | /* a failed connection is marked for closure to prevent (bad) re-use or |
| - | similar */ |
| - | connclose(conn, "TLS handshake failed"); |
| - | } |
| - | return result; |
| - | #else |
| - | (void) data; |
| - | (void) sockindex; |
| - | return CURLE_NOT_BUILT_IN; |
| - | #endif |
| + | return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED); |
| } | |
| - | CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) |
| + | static bool tunnel_is_failed(struct tunnel_state *ts) |
| { | |
| - | struct connectdata *conn = data->conn; |
| - | if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { |
| - | const CURLcode result = https_proxy_connect(data, sockindex); |
| - | if(result) |
| - | return result; |
| - | if(!conn->bits.proxy_ssl_connected[sockindex]) |
| - | return result; /* wait for HTTPS proxy SSL initialization to complete */ |
| - | } |
| - | |
| - | if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { |
| - | #ifndef CURL_DISABLE_PROXY |
| - | /* for [protocol] tunneled through HTTP proxy */ |
| - | const char *hostname; |
| - | int remote_port; |
| - | CURLcode result; |
| - | |
| - | /* We want "seamless" operations through HTTP proxy tunnel */ |
| - | |
| - | /* for the secondary socket (FTP), use the "connect to host" |
| - | * but ignore the "connect to port" (use the secondary port) |
| - | */ |
| - | |
| - | if(conn->bits.conn_to_host) |
| - | hostname = conn->conn_to_host.name; |
| - | else if(sockindex == SECONDARYSOCKET) |
| - | hostname = conn->secondaryhostname; |
| - | else |
| - | hostname = conn->host.name; |
| - | |
| - | if(sockindex == SECONDARYSOCKET) |
| - | remote_port = conn->secondary_port; |
| - | else if(conn->bits.conn_to_port) |
| - | remote_port = conn->conn_to_port; |
| - | else |
| - | remote_port = conn->remote_port; |
| - | |
| - | result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); |
| - | if(CURLE_OK != result) |
| - | return result; |
| - | Curl_safefree(data->state.aptr.proxyuserpwd); |
| - | #else |
| - | return CURLE_NOT_BUILT_IN; |
| - | #endif |
| - | } |
| - | /* no HTTP tunnel proxy, just return */ |
| - | return CURLE_OK; |
| + | return ts && (ts->tunnel_state == TUNNEL_FAILED); |
| } | |
| - | bool Curl_connect_complete(struct connectdata *conn) |
| + | static CURLcode tunnel_reinit(struct tunnel_state *ts, |
| + | struct connectdata *conn, |
| + | struct Curl_easy *data) |
| { | |
| - | return !conn->connect_state || |
| - | (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE); |
| - | } |
| + | (void)data; |
| + | DEBUGASSERT(ts); |
| + | Curl_dyn_reset(&ts->rcvbuf); |
| + | Curl_dyn_reset(&ts->req); |
| + | ts->tunnel_state = TUNNEL_INIT; |
| + | ts->keepon = KEEPON_CONNECT; |
| + | ts->cl = 0; |
| + | ts->close_connection = FALSE; |
| + | |
| + | if(conn->bits.conn_to_host) |
| + | ts->hostname = conn->conn_to_host.name; |
| + | else if(ts->sockindex == SECONDARYSOCKET) |
| + | ts->hostname = conn->secondaryhostname; |
| + | else |
| + | ts->hostname = conn->host.name; |
| + | |
| + | if(ts->sockindex == SECONDARYSOCKET) |
| + | ts->remote_port = conn->secondary_port; |
| + | else if(conn->bits.conn_to_port) |
| + | ts->remote_port = conn->conn_to_port; |
| + | else |
| + | ts->remote_port = conn->remote_port; |
| - | bool Curl_connect_ongoing(struct connectdata *conn) |
| - | { |
| - | return conn->connect_state && |
| - | (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE); |
| + | return CURLE_OK; |
| } | |
| - | /* 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. */ |
| - | int Curl_connect_getsock(struct connectdata *conn) |
| + | static CURLcode tunnel_init(struct tunnel_state **pts, |
| + | struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| { | |
| - | struct HTTP *http; |
| - | DEBUGASSERT(conn); |
| - | DEBUGASSERT(conn->connect_state); |
| - | http = &conn->connect_state->http_proxy; |
| - | |
| - | if(http->sending == HTTPSEND_REQUEST) |
| - | return GETSOCK_WRITESOCK(0); |
| - | |
| - | return GETSOCK_READSOCK(0); |
| - | } |
| + | struct tunnel_state *ts; |
| + | CURLcode result; |
| - | static CURLcode connect_init(struct Curl_easy *data, bool reinit) |
| - | { |
| - | struct http_connect_state *s; |
| - | struct connectdata *conn = data->conn; |
| if(conn->handler->flags & PROTOPT_NOTCPPROXY) { | |
| failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); | |
| return CURLE_UNSUPPORTED_PROTOCOL; | |
| } | |
| - | if(!reinit) { |
| - | CURLcode result; |
| - | DEBUGASSERT(!conn->connect_state); |
| - | /* we might need the upload buffer for streaming a partial request */ |
| - | result = Curl_get_upload_buffer(data); |
| - | if(result) |
| - | return result; |
| - | s = calloc(1, sizeof(struct http_connect_state)); |
| - | if(!s) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | infof(data, "allocate connect buffer"); |
| - | conn->connect_state = s; |
| - | Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); |
| - | |
| - | /* 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. |
| - | */ |
| - | s->prot_save = data->req.p.http; |
| - | data->req.p.http = &s->http_proxy; |
| - | connkeep(conn, "HTTP proxy CONNECT"); |
| - | } |
| - | else { |
| - | DEBUGASSERT(conn->connect_state); |
| - | s = conn->connect_state; |
| - | Curl_dyn_reset(&s->rcvbuf); |
| - | } |
| - | s->tunnel_state = TUNNEL_INIT; |
| - | s->keepon = KEEPON_CONNECT; |
| - | s->cl = 0; |
| - | s->close_connection = FALSE; |
| - | return CURLE_OK; |
| + | /* we might need the upload buffer for streaming a partial request */ |
| + | result = Curl_get_upload_buffer(data); |
| + | if(result) |
| + | return result; |
| + | |
| + | ts = calloc(1, sizeof(*ts)); |
| + | if(!ts) |
| + | return CURLE_OUT_OF_MEMORY; |
| + | |
| + | ts->sockindex = sockindex; |
| + | infof(data, "allocate connect buffer"); |
| + | |
| + | 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); |
| } | |
| - | void Curl_connect_done(struct Curl_easy *data) |
| + | static void tunnel_go_state(struct connectdata *conn, |
| + | struct tunnel_state *ts, |
| + | tunnel_state new_state, |
| + | struct Curl_easy *data) |
| { | |
| - | struct connectdata *conn = data->conn; |
| - | struct http_connect_state *s = conn->connect_state; |
| - | if(s && (s->tunnel_state != TUNNEL_EXIT)) { |
| - | s->tunnel_state = TUNNEL_EXIT; |
| - | Curl_dyn_free(&s->rcvbuf); |
| - | Curl_dyn_free(&s->req); |
| + | if(ts->tunnel_state == new_state) |
| + | return; |
| + | DEBUGF(infof(data, "http-proxy: tunnel %p go_state %d -> %d", |
| + | ts, ts->tunnel_state, new_state)); |
| + | /* leaving this one */ |
| + | switch(ts->tunnel_state) { |
| + | case TUNNEL_CONNECT: |
| + | data->req.ignorebody = FALSE; |
| + | break; |
| + | default: |
| + | break; |
| + | } |
| + | /* entering this one */ |
| + | switch(new_state) { |
| + | case TUNNEL_INIT: |
| + | tunnel_reinit(ts, conn, data); |
| + | break; |
| + | |
| + | case TUNNEL_CONNECT: |
| + | ts->tunnel_state = TUNNEL_CONNECT; |
| + | ts->keepon = KEEPON_CONNECT; |
| + | Curl_dyn_reset(&ts->rcvbuf); |
| + | break; |
| + | |
| + | case TUNNEL_RECEIVE: |
| + | ts->tunnel_state = TUNNEL_RECEIVE; |
| + | break; |
| + | case TUNNEL_RESPONSE: |
| + | ts->tunnel_state = TUNNEL_RESPONSE; |
| + | break; |
| + | |
| + | case TUNNEL_ESTABLISHED: |
| + | infof(data, "CONNECT phase completed"); |
| + | if(conn) |
| + | /* make sure this isn't set for the document request */ |
| + | conn->bits.rewindaftersend = FALSE; |
| + | data->state.authproxy.done = TRUE; |
| + | data->state.authproxy.multipass = FALSE; |
| + | /* FALLTHROUGH */ |
| + | case TUNNEL_FAILED: |
| + | ts->tunnel_state = new_state; |
| + | Curl_dyn_reset(&ts->rcvbuf); |
| + | Curl_dyn_reset(&ts->req); |
| /* restore the protocol pointer */ | |
| - | data->req.p.http = s->prot_save; |
| + | data->req.p.http = ts->prot_save; |
| data->info.httpcode = 0; /* clear it as it might've been used for the | |
| proxy */ | |
| - | data->req.ignorebody = FALSE; |
| + | /* If a proxy-authorization header was used for the proxy, then we should |
| + | make sure that it isn't accidentally used for the document request |
| + | after we've connected. So let's free and clear it here. */ |
| + | Curl_safefree(data->state.aptr.proxyuserpwd); |
| + | data->state.aptr.proxyuserpwd = NULL; |
| #ifdef USE_HYPER | |
| data->state.hconnect = FALSE; | |
| #endif | |
| - | infof(data, "CONNECT phase completed"); |
| + | break; |
| + | } |
| + | } |
| + | |
| + | static void tunnel_free(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | struct tunnel_state *ts = cf->ctx; |
| + | if(ts) { |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_FAILED, data); |
| + | Curl_dyn_free(&ts->rcvbuf); |
| + | Curl_dyn_free(&ts->req); |
| + | free(ts); |
| + | cf->ctx = NULL; |
| } | |
| } | |
| @@ -257,821 +276,916 @@ static CURLcode CONNECT_host(struct Curl_easy *data, | |
| } | |
| #ifndef USE_HYPER | |
| - | static CURLcode CONNECT(struct Curl_easy *data, |
| - | int sockindex, |
| - | const char *hostname, |
| - | int remote_port) |
| + | static CURLcode start_CONNECT(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts) |
| { | |
| - | int subversion = 0; |
| - | struct SingleRequest *k = &data->req; |
| + | char *hostheader = NULL; |
| + | char *host = NULL; |
| + | const char *httpv; |
| CURLcode result; | |
| - | struct connectdata *conn = data->conn; |
| - | curl_socket_t tunnelsocket = conn->sock[sockindex]; |
| - | struct http_connect_state *s = conn->connect_state; |
| - | struct HTTP *http = data->req.p.http; |
| - | char *linep; |
| - | size_t perline; |
| - | #define SELECT_OK 0 |
| - | #define SELECT_ERROR 1 |
| + | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
| + | ts->hostname, ts->remote_port); |
| + | |
| + | /* This only happens if we've looped here due to authentication |
| + | reasons, and we don't really use the newly cloned URL here |
| + | then. Just free() it. */ |
| + | Curl_safefree(data->req.newurl); |
| + | |
| + | result = CONNECT_host(data, conn, |
| + | ts->hostname, ts->remote_port, |
| + | &hostheader, &host); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* Setup the proxy-authorization header, if any */ |
| + | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
| + | hostheader, TRUE); |
| + | if(result) |
| + | goto out; |
| + | |
| + | httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; |
| + | |
| + | result = |
| + | Curl_dyn_addf(&ts->req, |
| + | "CONNECT %s HTTP/%s\r\n" |
| + | "%s" /* Host: */ |
| + | "%s", /* Proxy-Authorization */ |
| + | hostheader, |
| + | httpv, |
| + | host?host:"", |
| + | data->state.aptr.proxyuserpwd? |
| + | data->state.aptr.proxyuserpwd:""); |
| + | if(result) |
| + | goto out; |
| + | |
| + | if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) |
| + | && data->set.str[STRING_USERAGENT]) |
| + | result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n", |
| + | data->set.str[STRING_USERAGENT]); |
| + | if(result) |
| + | goto out; |
| + | |
| + | if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) |
| + | result = Curl_dyn_addn(&ts->req, |
| + | STRCONST("Proxy-Connection: Keep-Alive\r\n")); |
| + | if(result) |
| + | goto out; |
| + | |
| + | result = Curl_add_custom_headers(data, TRUE, &ts->req); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* CRLF terminate the request */ |
| + | result = Curl_dyn_addn(&ts->req, STRCONST("\r\n")); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* Send the connect request to the proxy */ |
| + | result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0, |
| + | ts->sockindex); |
| + | ts->headerlines = 0; |
| + | |
| + | out: |
| + | if(result) |
| + | failf(data, "Failed sending CONNECT to proxy"); |
| + | free(host); |
| + | free(hostheader); |
| + | return result; |
| + | } |
| + | |
| + | static CURLcode send_CONNECT(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts, |
| + | bool *done) |
| + | { |
| + | struct SingleRequest *k = &data->req; |
| + | struct HTTP *http = data->req.p.http; |
| + | CURLcode result = CURLE_OK; |
| - | if(Curl_connect_complete(conn)) |
| - | return CURLE_OK; /* CONNECT is already completed */ |
| + | if(http->sending != HTTPSEND_REQUEST) |
| + | goto out; |
| - | conn->bits.proxy_connect_closed = FALSE; |
| + | if(!ts->nsend) { |
| + | size_t fillcount; |
| + | k->upload_fromhere = data->state.ulbuf; |
| + | result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, |
| + | &fillcount); |
| + | if(result) |
| + | goto out; |
| + | ts->nsend = fillcount; |
| + | } |
| + | if(ts->nsend) { |
| + | ssize_t bytes_written; |
| + | /* write to socket (send away data) */ |
| + | result = Curl_write(data, |
| + | conn->writesockfd, /* socket to send to */ |
| + | k->upload_fromhere, /* buffer pointer */ |
| + | ts->nsend, /* buffer size */ |
| + | &bytes_written); /* actually sent */ |
| + | if(result) |
| + | goto out; |
| + | /* send to debug callback! */ |
| + | Curl_debug(data, CURLINFO_HEADER_OUT, |
| + | k->upload_fromhere, bytes_written); |
| - | do { |
| - | timediff_t check; |
| - | if(TUNNEL_INIT == s->tunnel_state) { |
| - | /* BEGIN CONNECT PHASE */ |
| - | struct dynbuf *req = &s->req; |
| - | char *hostheader = NULL; |
| - | char *host = NULL; |
| + | ts->nsend -= bytes_written; |
| + | k->upload_fromhere += bytes_written; |
| + | } |
| + | if(!ts->nsend) |
| + | http->sending = HTTPSEND_NADA; |
| - | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
| - | hostname, remote_port); |
| + | out: |
| + | if(result) |
| + | failf(data, "Failed sending CONNECT to proxy"); |
| + | *done = (http->sending != HTTPSEND_REQUEST); |
| + | return result; |
| + | } |
| - | /* This only happens if we've looped here due to authentication |
| - | reasons, and we don't really use the newly cloned URL here |
| - | then. Just free() it. */ |
| - | Curl_safefree(data->req.newurl); |
| + | static CURLcode on_resp_header(struct Curl_easy *data, |
| + | struct tunnel_state *ts, |
| + | const char *header) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | struct SingleRequest *k = &data->req; |
| + | int subversion = 0; |
| - | /* initialize send-buffer */ |
| - | Curl_dyn_init(req, DYN_HTTP_REQUEST); |
| + | if((checkprefix("WWW-Authenticate:", header) && |
| + | (401 == k->httpcode)) || |
| + | (checkprefix("Proxy-authenticate:", header) && |
| + | (407 == k->httpcode))) { |
| - | result = CONNECT_host(data, conn, |
| - | hostname, remote_port, &hostheader, &host); |
| - | if(result) |
| - | return result; |
| + | bool proxy = (k->httpcode == 407) ? TRUE : FALSE; |
| + | char *auth = Curl_copy_header_value(header); |
| + | if(!auth) |
| + | return CURLE_OUT_OF_MEMORY; |
| - | /* Setup the proxy-authorization header, if any */ |
| - | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
| - | hostheader, TRUE); |
| - | |
| - | if(!result) { |
| - | const char *httpv = |
| - | (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; |
| - | |
| - | result = |
| - | Curl_dyn_addf(req, |
| - | "CONNECT %s HTTP/%s\r\n" |
| - | "%s" /* Host: */ |
| - | "%s", /* Proxy-Authorization */ |
| - | hostheader, |
| - | httpv, |
| - | host?host:"", |
| - | data->state.aptr.proxyuserpwd? |
| - | data->state.aptr.proxyuserpwd:""); |
| - | |
| - | if(!result && !Curl_checkProxyheaders(data, |
| - | conn, STRCONST("User-Agent")) && |
| - | data->set.str[STRING_USERAGENT]) |
| - | result = Curl_dyn_addf(req, "User-Agent: %s\r\n", |
| - | data->set.str[STRING_USERAGENT]); |
| - | |
| - | if(!result && !Curl_checkProxyheaders(data, conn, |
| - | STRCONST("Proxy-Connection"))) |
| - | result = Curl_dyn_addn(req, |
| - | STRCONST("Proxy-Connection: Keep-Alive\r\n")); |
| - | |
| - | if(!result) |
| - | result = Curl_add_custom_headers(data, TRUE, req); |
| - | |
| - | if(!result) |
| - | /* CRLF terminate the request */ |
| - | result = Curl_dyn_addn(req, STRCONST("\r\n")); |
| - | |
| - | if(!result) { |
| - | /* Send the connect request to the proxy */ |
| - | result = Curl_buffer_send(req, data, &data->info.request_size, 0, |
| - | sockindex); |
| - | s->headerlines = 0; |
| - | } |
| - | if(result) |
| - | failf(data, "Failed sending CONNECT to proxy"); |
| - | } |
| - | free(host); |
| - | free(hostheader); |
| - | if(result) |
| - | return result; |
| + | DEBUGF(infof(data, "CONNECT: fwd auth header '%s'", |
| + | header)); |
| + | result = Curl_http_input_auth(data, proxy, auth); |
| - | s->tunnel_state = TUNNEL_CONNECT; |
| - | } /* END CONNECT PHASE */ |
| + | free(auth); |
| - | check = Curl_timeleft(data, NULL, TRUE); |
| - | if(check <= 0) { |
| - | failf(data, "Proxy CONNECT aborted due to timeout"); |
| - | return CURLE_OPERATION_TIMEDOUT; |
| + | if(result) |
| + | return result; |
| + | } |
| + | else if(checkprefix("Content-Length:", header)) { |
| + | if(k->httpcode/100 == 2) { |
| + | /* A client MUST ignore any Content-Length or Transfer-Encoding |
| + | header fields received in a successful response to CONNECT. |
| + | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
| + | infof(data, "Ignoring Content-Length in CONNECT %03d response", |
| + | k->httpcode); |
| + | } |
| + | else { |
| + | (void)curlx_strtoofft(header + strlen("Content-Length:"), |
| + | NULL, 10, &ts->cl); |
| + | } |
| + | } |
| + | else if(Curl_compareheader(header, |
| + | STRCONST("Connection:"), STRCONST("close"))) |
| + | ts->close_connection = TRUE; |
| + | else if(checkprefix("Transfer-Encoding:", header)) { |
| + | if(k->httpcode/100 == 2) { |
| + | /* A client MUST ignore any Content-Length or Transfer-Encoding |
| + | header fields received in a successful response to CONNECT. |
| + | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
| + | infof(data, "Ignoring Transfer-Encoding in " |
| + | "CONNECT %03d response", k->httpcode); |
| + | } |
| + | else if(Curl_compareheader(header, |
| + | STRCONST("Transfer-Encoding:"), |
| + | STRCONST("chunked"))) { |
| + | infof(data, "CONNECT responded chunked"); |
| + | ts->chunked_encoding = TRUE; |
| + | /* init our chunky engine */ |
| + | Curl_httpchunk_init(data); |
| } | |
| + | } |
| + | else if(Curl_compareheader(header, |
| + | STRCONST("Proxy-Connection:"), |
| + | STRCONST("close"))) |
| + | ts->close_connection = TRUE; |
| + | else if(2 == sscanf(header, "HTTP/1.%d %d", |
| + | &subversion, |
| + | &k->httpcode)) { |
| + | /* store the HTTP code from the proxy */ |
| + | data->info.httpproxycode = k->httpcode; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | static CURLcode recv_CONNECT_resp(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts, |
| + | bool *done) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | struct SingleRequest *k = &data->req; |
| + | curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; |
| + | char *linep; |
| + | size_t perline; |
| + | int error; |
| + | |
| + | #define SELECT_OK 0 |
| + | #define SELECT_ERROR 1 |
| + | |
| + | DEBUGF(infof(data, "CONNECT: recv response, keepon=%d", ts->keepon)); |
| + | error = SELECT_OK; |
| + | *done = FALSE; |
| + | |
| + | if(!Curl_conn_data_pending(data, ts->sockindex)) |
| + | return CURLE_OK; |
| + | |
| + | while(ts->keepon) { |
| + | ssize_t gotbytes; |
| + | char byte; |
| - | if(!Curl_conn_data_pending(conn, sockindex) && !http->sending) |
| - | /* return so we'll be called again polling-style */ |
| + | /* Read one byte at a time to avoid a race condition. Wait at most one |
| + | second before looping to ensure continuous pgrsUpdates. */ |
| + | result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); |
| + | if(result == CURLE_AGAIN) |
| + | /* socket buffer drained, return */ |
| return CURLE_OK; | |
| - | /* at this point, the tunnel_connecting phase is over. */ |
| - | |
| - | if(http->sending == HTTPSEND_REQUEST) { |
| - | if(!s->nsend) { |
| - | size_t fillcount; |
| - | k->upload_fromhere = data->state.ulbuf; |
| - | result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, |
| - | &fillcount); |
| - | if(result) |
| - | return result; |
| - | s->nsend = fillcount; |
| + | if(Curl_pgrsUpdate(data)) |
| + | return CURLE_ABORTED_BY_CALLBACK; |
| + | |
| + | if(result) { |
| + | ts->keepon = KEEPON_DONE; |
| + | break; |
| + | } |
| + | |
| + | if(gotbytes <= 0) { |
| + | if(data->set.proxyauth && data->state.authproxy.avail && |
| + | data->state.aptr.proxyuserpwd) { |
| + | /* proxy auth was requested and there was proxy auth available, |
| + | then deem this as "mere" proxy disconnect */ |
| + | ts->close_connection = TRUE; |
| + | infof(data, "Proxy CONNECT connection closed"); |
| } | |
| - | if(s->nsend) { |
| - | ssize_t bytes_written; |
| - | /* write to socket (send away data) */ |
| - | result = Curl_write(data, |
| - | conn->writesockfd, /* socket to send to */ |
| - | k->upload_fromhere, /* buffer pointer */ |
| - | s->nsend, /* buffer size */ |
| - | &bytes_written); /* actually sent */ |
| - | |
| - | if(!result) |
| - | /* send to debug callback! */ |
| - | Curl_debug(data, CURLINFO_HEADER_OUT, |
| - | k->upload_fromhere, bytes_written); |
| - | |
| - | s->nsend -= bytes_written; |
| - | k->upload_fromhere += bytes_written; |
| - | return result; |
| + | else { |
| + | error = SELECT_ERROR; |
| + | failf(data, "Proxy CONNECT aborted"); |
| } | |
| - | http->sending = HTTPSEND_NADA; |
| - | /* if nothing left to send, continue */ |
| + | ts->keepon = KEEPON_DONE; |
| + | break; |
| } | |
| - | { /* READING RESPONSE PHASE */ |
| - | int error = SELECT_OK; |
| - | |
| - | while(s->keepon) { |
| - | ssize_t gotbytes; |
| - | char byte; |
| - | |
| - | /* Read one byte at a time to avoid a race condition. Wait at most one |
| - | second before looping to ensure continuous pgrsUpdates. */ |
| - | result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); |
| - | if(result == CURLE_AGAIN) |
| - | /* socket buffer drained, return */ |
| - | return CURLE_OK; |
| - | if(Curl_pgrsUpdate(data)) |
| - | return CURLE_ABORTED_BY_CALLBACK; |
| + | if(ts->keepon == KEEPON_IGNORE) { |
| + | /* This means we are currently ignoring a response-body */ |
| - | if(result) { |
| - | s->keepon = KEEPON_DONE; |
| - | break; |
| - | } |
| - | else if(gotbytes <= 0) { |
| - | if(data->set.proxyauth && data->state.authproxy.avail && |
| - | data->state.aptr.proxyuserpwd) { |
| - | /* proxy auth was requested and there was proxy auth available, |
| - | then deem this as "mere" proxy disconnect */ |
| - | conn->bits.proxy_connect_closed = TRUE; |
| - | infof(data, "Proxy CONNECT connection closed"); |
| - | } |
| - | else { |
| - | error = SELECT_ERROR; |
| - | failf(data, "Proxy CONNECT aborted"); |
| - | } |
| - | s->keepon = KEEPON_DONE; |
| + | if(ts->cl) { |
| + | /* A Content-Length based body: simply count down the counter |
| + | and make sure to break out of the loop when we're done! */ |
| + | ts->cl--; |
| + | if(ts->cl <= 0) { |
| + | ts->keepon = KEEPON_DONE; |
| break; | |
| } | |
| - | |
| - | if(s->keepon == KEEPON_IGNORE) { |
| - | /* This means we are currently ignoring a response-body */ |
| - | |
| - | if(s->cl) { |
| - | /* A Content-Length based body: simply count down the counter |
| - | and make sure to break out of the loop when we're done! */ |
| - | s->cl--; |
| - | if(s->cl <= 0) { |
| - | s->keepon = KEEPON_DONE; |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| - | break; |
| - | } |
| - | } |
| - | else { |
| - | /* chunked-encoded body, so we need to do the chunked dance |
| - | properly to know when the end of the body is reached */ |
| - | CHUNKcode r; |
| - | CURLcode extra; |
| - | ssize_t tookcareof = 0; |
| - | |
| - | /* now parse the chunked piece of data so that we can |
| - | properly tell when the stream ends */ |
| - | r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); |
| - | if(r == CHUNKE_STOP) { |
| - | /* we're done reading chunks! */ |
| - | infof(data, "chunk reading DONE"); |
| - | s->keepon = KEEPON_DONE; |
| - | /* we did the full CONNECT treatment, go COMPLETE */ |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| - | } |
| - | } |
| - | continue; |
| - | } |
| - | |
| - | if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) { |
| - | failf(data, "CONNECT response too large"); |
| - | return CURLE_RECV_ERROR; |
| + | } |
| + | else { |
| + | /* chunked-encoded body, so we need to do the chunked dance |
| + | properly to know when the end of the body is reached */ |
| + | CHUNKcode r; |
| + | CURLcode extra; |
| + | ssize_t tookcareof = 0; |
| + | |
| + | /* now parse the chunked piece of data so that we can |
| + | properly tell when the stream ends */ |
| + | r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); |
| + | if(r == CHUNKE_STOP) { |
| + | /* we're done reading chunks! */ |
| + | infof(data, "chunk reading DONE"); |
| + | ts->keepon = KEEPON_DONE; |
| } | |
| + | } |
| + | continue; |
| + | } |
| - | /* if this is not the end of a header line then continue */ |
| - | if(byte != 0x0a) |
| - | continue; |
| - | |
| - | s->headerlines++; |
| - | linep = Curl_dyn_ptr(&s->rcvbuf); |
| - | perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ |
| - | |
| - | /* output debug if that is requested */ |
| - | Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); |
| + | if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { |
| + | failf(data, "CONNECT response too large"); |
| + | return CURLE_RECV_ERROR; |
| + | } |
| - | if(!data->set.suppress_connect_headers) { |
| - | /* send the header to the callback */ |
| - | int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | |
| - | (data->set.include_header ? CLIENTWRITE_BODY : 0) | |
| - | (s->headerlines == 1 ? CLIENTWRITE_STATUS : 0); |
| + | /* if this is not the end of a header line then continue */ |
| + | if(byte != 0x0a) |
| + | continue; |
| - | result = Curl_client_write(data, writetype, linep, perline); |
| - | if(result) |
| - | return result; |
| - | } |
| + | ts->headerlines++; |
| + | linep = Curl_dyn_ptr(&ts->rcvbuf); |
| + | perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ |
| - | data->info.header_size += (long)perline; |
| - | |
| - | /* Newlines are CRLF, so the CR is ignored as the line isn't |
| - | really terminated until the LF comes. Treat a following CR |
| - | as end-of-headers as well.*/ |
| - | |
| - | if(('\r' == linep[0]) || |
| - | ('\n' == linep[0])) { |
| - | /* end of response-headers from the proxy */ |
| - | |
| - | if((407 == k->httpcode) && !data->state.authproblem) { |
| - | /* If we get a 407 response code with content length |
| - | when we have no auth problem, we must ignore the |
| - | whole response-body */ |
| - | s->keepon = KEEPON_IGNORE; |
| - | |
| - | if(s->cl) { |
| - | infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T |
| - | " bytes of response-body", s->cl); |
| - | } |
| - | else if(s->chunked_encoding) { |
| - | CHUNKcode r; |
| - | CURLcode extra; |
| - | |
| - | infof(data, "Ignore chunked response-body"); |
| - | |
| - | /* We set ignorebody true here since the chunked decoder |
| - | function will acknowledge that. Pay attention so that this is |
| - | cleared again when this function returns! */ |
| - | k->ignorebody = TRUE; |
| - | |
| - | if(linep[1] == '\n') |
| - | /* this can only be a LF if the letter at index 0 was a CR */ |
| - | linep++; |
| - | |
| - | /* now parse the chunked piece of data so that we can properly |
| - | tell when the stream ends */ |
| - | r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, |
| - | &extra); |
| - | if(r == CHUNKE_STOP) { |
| - | /* we're done reading chunks! */ |
| - | infof(data, "chunk reading DONE"); |
| - | s->keepon = KEEPON_DONE; |
| - | /* we did the full CONNECT treatment, go to COMPLETE */ |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| - | } |
| - | } |
| - | else { |
| - | /* 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 */ |
| - | s->keepon = KEEPON_DONE; |
| - | } |
| - | } |
| - | else |
| - | s->keepon = KEEPON_DONE; |
| + | /* output debug if that is requested */ |
| + | Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); |
| - | if(s->keepon == KEEPON_DONE && !s->cl) |
| - | /* we did the full CONNECT treatment, go to COMPLETE */ |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| + | if(!data->set.suppress_connect_headers) { |
| + | /* send the header to the callback */ |
| + | int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | |
| + | (data->set.include_header ? CLIENTWRITE_BODY : 0) | |
| + | (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); |
| - | DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE); |
| - | continue; |
| - | } |
| + | result = Curl_client_write(data, writetype, linep, perline); |
| + | if(result) |
| + | return result; |
| + | } |
| - | if((checkprefix("WWW-Authenticate:", linep) && |
| - | (401 == k->httpcode)) || |
| - | (checkprefix("Proxy-authenticate:", linep) && |
| - | (407 == k->httpcode))) { |
| + | data->info.header_size += (long)perline; |
| - | bool proxy = (k->httpcode == 407) ? TRUE : FALSE; |
| - | char *auth = Curl_copy_header_value(linep); |
| - | if(!auth) |
| - | return CURLE_OUT_OF_MEMORY; |
| + | /* Newlines are CRLF, so the CR is ignored as the line isn't |
| + | really terminated until the LF comes. Treat a following CR |
| + | as end-of-headers as well.*/ |
| - | result = Curl_http_input_auth(data, proxy, auth); |
| + | if(('\r' == linep[0]) || |
| + | ('\n' == linep[0])) { |
| + | /* end of response-headers from the proxy */ |
| - | free(auth); |
| + | if((407 == k->httpcode) && !data->state.authproblem) { |
| + | /* If we get a 407 response code with content length |
| + | when we have no auth problem, we must ignore the |
| + | whole response-body */ |
| + | ts->keepon = KEEPON_IGNORE; |
| - | if(result) |
| - | return result; |
| - | } |
| - | else if(checkprefix("Content-Length:", linep)) { |
| - | if(k->httpcode/100 == 2) { |
| - | /* A client MUST ignore any Content-Length or Transfer-Encoding |
| - | header fields received in a successful response to CONNECT. |
| - | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
| - | infof(data, "Ignoring Content-Length in CONNECT %03d response", |
| - | k->httpcode); |
| - | } |
| - | else { |
| - | (void)curlx_strtoofft(linep + |
| - | strlen("Content-Length:"), NULL, 10, &s->cl); |
| - | } |
| + | if(ts->cl) { |
| + | infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T |
| + | " bytes of response-body", ts->cl); |
| } | |
| - | else if(Curl_compareheader(linep, |
| - | STRCONST("Connection:"), STRCONST("close"))) |
| - | s->close_connection = TRUE; |
| - | else if(checkprefix("Transfer-Encoding:", linep)) { |
| - | if(k->httpcode/100 == 2) { |
| - | /* A client MUST ignore any Content-Length or Transfer-Encoding |
| - | header fields received in a successful response to CONNECT. |
| - | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
| - | infof(data, "Ignoring Transfer-Encoding in " |
| - | "CONNECT %03d response", k->httpcode); |
| - | } |
| - | else if(Curl_compareheader(linep, |
| - | STRCONST("Transfer-Encoding:"), |
| - | STRCONST("chunked"))) { |
| - | infof(data, "CONNECT responded chunked"); |
| - | s->chunked_encoding = TRUE; |
| - | /* init our chunky engine */ |
| - | Curl_httpchunk_init(data); |
| + | else if(ts->chunked_encoding) { |
| + | CHUNKcode r; |
| + | CURLcode extra; |
| + | |
| + | infof(data, "Ignore chunked response-body"); |
| + | |
| + | /* We set ignorebody true here since the chunked decoder |
| + | function will acknowledge that. Pay attention so that this is |
| + | cleared again when this function returns! */ |
| + | k->ignorebody = TRUE; |
| + | |
| + | if(linep[1] == '\n') |
| + | /* this can only be a LF if the letter at index 0 was a CR */ |
| + | linep++; |
| + | |
| + | /* now parse the chunked piece of data so that we can properly |
| + | tell when the stream ends */ |
| + | r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, |
| + | &extra); |
| + | if(r == CHUNKE_STOP) { |
| + | /* we're done reading chunks! */ |
| + | infof(data, "chunk reading DONE"); |
| + | ts->keepon = KEEPON_DONE; |
| } | |
| } | |
| - | else if(Curl_compareheader(linep, |
| - | STRCONST("Proxy-Connection:"), |
| - | STRCONST("close"))) |
| - | s->close_connection = TRUE; |
| - | else if(2 == sscanf(linep, "HTTP/1.%d %d", |
| - | &subversion, |
| - | &k->httpcode)) { |
| - | /* store the HTTP code from the proxy */ |
| - | data->info.httpproxycode = k->httpcode; |
| + | else { |
| + | /* 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")); |
| + | ts->keepon = KEEPON_DONE; |
| } | |
| - | |
| - | Curl_dyn_reset(&s->rcvbuf); |
| - | } /* while there's buffer left and loop is requested */ |
| - | |
| - | if(Curl_pgrsUpdate(data)) |
| - | return CURLE_ABORTED_BY_CALLBACK; |
| - | |
| - | if(error) |
| - | return CURLE_RECV_ERROR; |
| - | |
| - | if(data->info.httpproxycode/100 != 2) { |
| - | /* Deal with the possibly already received authenticate |
| - | headers. 'newurl' is set to a new URL if we must loop. */ |
| - | result = Curl_http_auth_act(data); |
| - | if(result) |
| - | return result; |
| - | |
| - | if(conn->bits.close) |
| - | /* the connection has been marked for closure, most likely in the |
| - | Curl_http_auth_act() function and thus we can kill it at once |
| - | below */ |
| - | s->close_connection = TRUE; |
| } | |
| - | |
| - | if(s->close_connection && data->req.newurl) { |
| - | /* Connection closed by server. Don't use it anymore */ |
| - | Curl_closesocket(data, conn, conn->sock[sockindex]); |
| - | conn->sock[sockindex] = CURL_SOCKET_BAD; |
| - | break; |
| + | else { |
| + | DEBUGF(infof(data, "CONNECT: no end of response headers")); |
| + | ts->keepon = KEEPON_DONE; |
| } | |
| - | } /* END READING RESPONSE PHASE */ |
| - | /* If we are supposed to continue and request a new URL, which basically |
| - | * means the HTTP authentication is still going on so if the tunnel |
| - | * is complete we start over in INIT state */ |
| - | if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { |
| - | connect_init(data, TRUE); /* reinit */ |
| + | DEBUGASSERT(ts->keepon == KEEPON_IGNORE |
| + | || ts->keepon == KEEPON_DONE); |
| + | continue; |
| } | |
| - | } while(data->req.newurl); |
| - | |
| - | if(data->info.httpproxycode/100 != 2) { |
| - | if(s->close_connection && data->req.newurl) { |
| - | conn->bits.proxy_connect_closed = TRUE; |
| - | infof(data, "Connect me again please"); |
| - | Curl_connect_done(data); |
| - | } |
| - | else { |
| - | free(data->req.newurl); |
| - | data->req.newurl = NULL; |
| - | /* failure, close this connection to avoid re-use */ |
| - | streamclose(conn, "proxy CONNECT failure"); |
| - | } |
| + | result = on_resp_header(data, ts, linep); |
| + | if(result) |
| + | return result; |
| - | /* to back to init state */ |
| - | s->tunnel_state = TUNNEL_INIT; |
| + | Curl_dyn_reset(&ts->rcvbuf); |
| + | } /* while there's buffer left and loop is requested */ |
| - | if(conn->bits.proxy_connect_closed) |
| - | /* this is not an error, just part of the connection negotiation */ |
| - | return CURLE_OK; |
| - | Curl_dyn_free(&s->rcvbuf); |
| - | failf(data, "Received HTTP code %d from proxy after CONNECT", |
| - | data->req.httpcode); |
| - | return CURLE_RECV_ERROR; |
| + | if(error) |
| + | result = CURLE_RECV_ERROR; |
| + | *done = (ts->keepon == KEEPON_DONE); |
| + | if(!result && *done && data->info.httpproxycode/100 != 2) { |
| + | /* Deal with the possibly already received authenticate |
| + | headers. 'newurl' is set to a new URL if we must loop. */ |
| + | result = Curl_http_auth_act(data); |
| } | |
| - | |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| - | |
| - | /* If a proxy-authorization header was used for the proxy, then we should |
| - | make sure that it isn't accidentally used for the document request |
| - | after we've connected. So let's free and clear it here. */ |
| - | Curl_safefree(data->state.aptr.proxyuserpwd); |
| - | data->state.aptr.proxyuserpwd = NULL; |
| - | |
| - | data->state.authproxy.done = TRUE; |
| - | data->state.authproxy.multipass = FALSE; |
| - | |
| - | infof(data, "Proxy replied %d to CONNECT request", |
| - | data->info.httpproxycode); |
| - | data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ |
| - | conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the |
| - | document request */ |
| - | Curl_dyn_free(&s->rcvbuf); |
| - | return CURLE_OK; |
| + | return result; |
| } | |
| + | |
| #else | |
| /* The Hyper version of CONNECT */ | |
| - | static CURLcode CONNECT(struct Curl_easy *data, |
| - | int sockindex, |
| - | const char *hostname, |
| - | int remote_port) |
| + | static CURLcode start_CONNECT(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts) |
| { | |
| - | struct connectdata *conn = data->conn; |
| struct hyptransfer *h = &data->hyp; | |
| - | curl_socket_t tunnelsocket = conn->sock[sockindex]; |
| - | struct http_connect_state *s = conn->connect_state; |
| - | CURLcode result = CURLE_OUT_OF_MEMORY; |
| + | curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; |
| hyper_io *io = NULL; | |
| hyper_request *req = NULL; | |
| hyper_headers *headers = NULL; | |
| hyper_clientconn_options *options = NULL; | |
| hyper_task *handshake = NULL; | |
| hyper_task *task = NULL; /* for the handshake */ | |
| - | hyper_task *sendtask = NULL; /* for the send */ |
| hyper_clientconn *client = NULL; | |
| - | hyper_error *hypererr = NULL; |
| + | hyper_task *sendtask = NULL; /* for the send */ |
| char *hostheader = NULL; /* for CONNECT */ | |
| char *host = NULL; /* Host: */ | |
| + | CURLcode result = CURLE_OUT_OF_MEMORY; |
| - | if(Curl_connect_complete(conn)) |
| - | return CURLE_OK; /* CONNECT is already completed */ |
| + | io = hyper_io_new(); |
| + | if(!io) { |
| + | failf(data, "Couldn't create hyper IO"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | /* tell Hyper how to read/write network data */ |
| + | hyper_io_set_userdata(io, data); |
| + | hyper_io_set_read(io, Curl_hyper_recv); |
| + | hyper_io_set_write(io, Curl_hyper_send); |
| + | conn->sockfd = tunnelsocket; |
| + | |
| + | data->state.hconnect = TRUE; |
| + | |
| + | /* create an executor to poll futures */ |
| + | if(!h->exec) { |
| + | h->exec = hyper_executor_new(); |
| + | if(!h->exec) { |
| + | failf(data, "Couldn't create hyper executor"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | } |
| - | conn->bits.proxy_connect_closed = FALSE; |
| + | options = hyper_clientconn_options_new(); |
| + | hyper_clientconn_options_set_preserve_header_case(options, 1); |
| + | hyper_clientconn_options_set_preserve_header_order(options, 1); |
| - | do { |
| - | switch(s->tunnel_state) { |
| - | case TUNNEL_INIT: |
| - | /* BEGIN CONNECT PHASE */ |
| - | io = hyper_io_new(); |
| - | if(!io) { |
| - | failf(data, "Couldn't create hyper IO"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | /* tell Hyper how to read/write network data */ |
| - | hyper_io_set_userdata(io, data); |
| - | hyper_io_set_read(io, Curl_hyper_recv); |
| - | hyper_io_set_write(io, Curl_hyper_send); |
| - | conn->sockfd = tunnelsocket; |
| - | |
| - | data->state.hconnect = TRUE; |
| - | |
| - | /* create an executor to poll futures */ |
| - | if(!h->exec) { |
| - | h->exec = hyper_executor_new(); |
| - | if(!h->exec) { |
| - | failf(data, "Couldn't create hyper executor"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | } |
| + | if(!options) { |
| + | failf(data, "Couldn't create hyper client options"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| - | options = hyper_clientconn_options_new(); |
| - | hyper_clientconn_options_set_preserve_header_case(options, 1); |
| - | hyper_clientconn_options_set_preserve_header_order(options, 1); |
| + | hyper_clientconn_options_exec(options, h->exec); |
| - | if(!options) { |
| - | failf(data, "Couldn't create hyper client options"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | /* "Both the `io` and the `options` are consumed in this function |
| + | call" */ |
| + | handshake = hyper_clientconn_handshake(io, options); |
| + | if(!handshake) { |
| + | failf(data, "Couldn't create hyper client handshake"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | io = NULL; |
| + | options = NULL; |
| - | hyper_clientconn_options_exec(options, h->exec); |
| + | if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { |
| + | failf(data, "Couldn't hyper_executor_push the handshake"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | handshake = NULL; /* ownership passed on */ |
| - | /* "Both the `io` and the `options` are consumed in this function |
| - | call" */ |
| - | handshake = hyper_clientconn_handshake(io, options); |
| - | if(!handshake) { |
| - | failf(data, "Couldn't create hyper client handshake"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | io = NULL; |
| - | options = NULL; |
| + | task = hyper_executor_poll(h->exec); |
| + | if(!task) { |
| + | failf(data, "Couldn't hyper_executor_poll the handshake"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| - | if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { |
| - | failf(data, "Couldn't hyper_executor_push the handshake"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | handshake = NULL; /* ownership passed on */ |
| + | client = hyper_task_value(task); |
| + | hyper_task_free(task); |
| + | req = hyper_request_new(); |
| + | if(!req) { |
| + | failf(data, "Couldn't hyper_request_new"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | if(hyper_request_set_method(req, (uint8_t *)"CONNECT", |
| + | strlen("CONNECT"))) { |
| + | failf(data, "error setting method"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| - | task = hyper_executor_poll(h->exec); |
| - | if(!task) { |
| - | failf(data, "Couldn't hyper_executor_poll the handshake"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
| + | ts->hostname, ts->remote_port); |
| - | client = hyper_task_value(task); |
| - | hyper_task_free(task); |
| - | req = hyper_request_new(); |
| - | if(!req) { |
| - | failf(data, "Couldn't hyper_request_new"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | if(hyper_request_set_method(req, (uint8_t *)"CONNECT", |
| - | strlen("CONNECT"))) { |
| - | failf(data, "error setting method"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | /* This only happens if we've looped here due to authentication |
| + | reasons, and we don't really use the newly cloned URL here |
| + | then. Just free() it. */ |
| + | Curl_safefree(data->req.newurl); |
| - | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
| - | hostname, remote_port); |
| + | result = CONNECT_host(data, conn, ts->hostname, ts->remote_port, |
| + | &hostheader, &host); |
| + | if(result) |
| + | goto error; |
| - | /* This only happens if we've looped here due to authentication |
| - | reasons, and we don't really use the newly cloned URL here |
| - | then. Just free() it. */ |
| - | Curl_safefree(data->req.newurl); |
| + | if(hyper_request_set_uri(req, (uint8_t *)hostheader, |
| + | strlen(hostheader))) { |
| + | failf(data, "error setting path"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | if(data->set.verbose) { |
| + | char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); |
| + | if(!se) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); |
| + | free(se); |
| + | } |
| + | /* Setup the proxy-authorization header, if any */ |
| + | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
| + | hostheader, TRUE); |
| + | if(result) |
| + | goto error; |
| + | Curl_safefree(hostheader); |
| + | |
| + | /* default is 1.1 */ |
| + | if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && |
| + | (HYPERE_OK != hyper_request_set_version(req, |
| + | HYPER_HTTP_VERSION_1_0))) { |
| + | failf(data, "error setting HTTP version"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| - | result = CONNECT_host(data, conn, hostname, remote_port, |
| - | &hostheader, &host); |
| - | if(result) |
| - | goto error; |
| + | headers = hyper_request_headers(req); |
| + | if(!headers) { |
| + | failf(data, "hyper_request_headers"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | if(host) { |
| + | result = Curl_hyper_header(data, headers, host); |
| + | if(result) |
| + | goto error; |
| + | Curl_safefree(host); |
| + | } |
| - | if(hyper_request_set_uri(req, (uint8_t *)hostheader, |
| - | strlen(hostheader))) { |
| - | failf(data, "error setting path"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | if(data->set.verbose) { |
| - | char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); |
| - | if(!se) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); |
| - | free(se); |
| - | } |
| - | /* Setup the proxy-authorization header, if any */ |
| - | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
| - | hostheader, TRUE); |
| - | if(result) |
| - | goto error; |
| - | Curl_safefree(hostheader); |
| + | if(data->state.aptr.proxyuserpwd) { |
| + | result = Curl_hyper_header(data, headers, |
| + | data->state.aptr.proxyuserpwd); |
| + | if(result) |
| + | goto error; |
| + | } |
| - | /* default is 1.1 */ |
| - | if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && |
| - | (HYPERE_OK != hyper_request_set_version(req, |
| - | HYPER_HTTP_VERSION_1_0))) { |
| - | failf(data, "error setting HTTP version"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && |
| + | data->set.str[STRING_USERAGENT]) { |
| + | struct dynbuf ua; |
| + | Curl_dyn_init(&ua, DYN_HTTP_REQUEST); |
| + | result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", |
| + | data->set.str[STRING_USERAGENT]); |
| + | if(result) |
| + | goto error; |
| + | result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); |
| + | if(result) |
| + | goto error; |
| + | Curl_dyn_free(&ua); |
| + | } |
| + | |
| + | if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { |
| + | result = Curl_hyper_header(data, headers, |
| + | "Proxy-Connection: Keep-Alive"); |
| + | if(result) |
| + | goto error; |
| + | } |
| + | |
| + | result = Curl_add_custom_headers(data, TRUE, headers); |
| + | if(result) |
| + | goto error; |
| + | |
| + | sendtask = hyper_clientconn_send(client, req); |
| + | if(!sendtask) { |
| + | failf(data, "hyper_clientconn_send"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | |
| + | if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { |
| + | failf(data, "Couldn't hyper_executor_push the send"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | |
| + | error: |
| + | free(host); |
| + | free(hostheader); |
| + | if(io) |
| + | hyper_io_free(io); |
| + | if(options) |
| + | hyper_clientconn_options_free(options); |
| + | if(handshake) |
| + | hyper_task_free(handshake); |
| + | if(client) |
| + | hyper_clientconn_free(client); |
| + | return result; |
| + | } |
| - | headers = hyper_request_headers(req); |
| - | if(!headers) { |
| - | failf(data, "hyper_request_headers"); |
| + | static CURLcode send_CONNECT(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts, |
| + | bool *done) |
| + | { |
| + | struct hyptransfer *h = &data->hyp; |
| + | hyper_task *task = NULL; |
| + | hyper_error *hypererr = NULL; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | (void)ts; |
| + | (void)conn; |
| + | do { |
| + | task = hyper_executor_poll(h->exec); |
| + | if(task) { |
| + | bool error = hyper_task_type(task) == HYPER_TASK_ERROR; |
| + | if(error) |
| + | hypererr = hyper_task_value(task); |
| + | hyper_task_free(task); |
| + | if(error) { |
| + | /* this could probably use a better error code? */ |
| result = CURLE_OUT_OF_MEMORY; | |
| goto error; | |
| } | |
| - | if(host) { |
| - | result = Curl_hyper_header(data, headers, host); |
| - | if(result) |
| - | goto error; |
| - | Curl_safefree(host); |
| - | } |
| + | } |
| + | } while(task); |
| + | error: |
| + | *done = (result == CURLE_OK); |
| + | if(hypererr) { |
| + | uint8_t errbuf[256]; |
| + | size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); |
| + | failf(data, "Hyper: %.*s", (int)errlen, errbuf); |
| + | hyper_error_free(hypererr); |
| + | } |
| + | return result; |
| + | } |
| - | if(data->state.aptr.proxyuserpwd) { |
| - | result = Curl_hyper_header(data, headers, |
| - | data->state.aptr.proxyuserpwd); |
| - | if(result) |
| - | goto error; |
| - | } |
| + | static CURLcode recv_CONNECT_resp(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | struct tunnel_state *ts, |
| + | bool *done) |
| + | { |
| + | struct hyptransfer *h = &data->hyp; |
| + | CURLcode result; |
| + | int didwhat; |
| + | |
| + | (void)ts; |
| + | *done = FALSE; |
| + | result = Curl_hyper_stream(data, conn, &didwhat, done, |
| + | CURL_CSELECT_IN | CURL_CSELECT_OUT); |
| + | if(result || !*done) |
| + | return result; |
| + | if(h->exec) { |
| + | hyper_executor_free(h->exec); |
| + | h->exec = NULL; |
| + | } |
| + | if(h->read_waker) { |
| + | hyper_waker_free(h->read_waker); |
| + | h->read_waker = NULL; |
| + | } |
| + | if(h->write_waker) { |
| + | hyper_waker_free(h->write_waker); |
| + | h->write_waker = NULL; |
| + | } |
| + | return result; |
| + | } |
| - | if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && |
| - | data->set.str[STRING_USERAGENT]) { |
| - | struct dynbuf ua; |
| - | Curl_dyn_init(&ua, DYN_HTTP_REQUEST); |
| - | result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", |
| - | data->set.str[STRING_USERAGENT]); |
| - | if(result) |
| - | goto error; |
| - | result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); |
| - | if(result) |
| - | goto error; |
| - | Curl_dyn_free(&ua); |
| - | } |
| + | #endif |
| - | if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { |
| - | result = Curl_hyper_header(data, headers, |
| - | "Proxy-Connection: Keep-Alive"); |
| - | if(result) |
| - | goto error; |
| - | } |
| + | static CURLcode CONNECT(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | struct tunnel_state *ts) |
| + | { |
| + | struct connectdata *conn = cf->conn; |
| + | CURLcode result; |
| + | bool done; |
| + | |
| + | if(tunnel_is_established(ts)) |
| + | return CURLE_OK; |
| + | if(tunnel_is_failed(ts)) |
| + | return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ |
| + | |
| + | do { |
| + | timediff_t check; |
| + | |
| + | check = Curl_timeleft(data, NULL, TRUE); |
| + | if(check <= 0) { |
| + | failf(data, "Proxy CONNECT aborted due to timeout"); |
| + | result = CURLE_OPERATION_TIMEDOUT; |
| + | goto out; |
| + | } |
| - | result = Curl_add_custom_headers(data, TRUE, headers); |
| + | 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); |
| if(result) | |
| - | goto error; |
| + | goto out; |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_CONNECT, data); |
| + | /* FALLTHROUGH */ |
| - | sendtask = hyper_clientconn_send(client, req); |
| - | if(!sendtask) { |
| - | failf(data, "hyper_clientconn_send"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | case TUNNEL_CONNECT: |
| + | /* see that the request is completely sent */ |
| + | result = send_CONNECT(data, cf->conn, ts, &done); |
| + | if(result || !done) |
| + | goto out; |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_RECEIVE, data); |
| + | /* FALLTHROUGH */ |
| - | if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { |
| - | failf(data, "Couldn't hyper_executor_push the send"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| + | case TUNNEL_RECEIVE: |
| + | /* read what is there */ |
| + | result = recv_CONNECT_resp(data, cf->conn, ts, &done); |
| + | if(Curl_pgrsUpdate(data)) { |
| + | result = CURLE_ABORTED_BY_CALLBACK; |
| + | goto out; |
| } | |
| + | /* error or not complete yet. return for more multi-multi */ |
| + | if(result || !done) |
| + | goto out; |
| + | /* got it */ |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_RESPONSE, data); |
| + | /* FALLTHROUGH */ |
| - | hyper_clientconn_free(client); |
| - | |
| - | do { |
| - | task = hyper_executor_poll(h->exec); |
| - | if(task) { |
| - | bool error = hyper_task_type(task) == HYPER_TASK_ERROR; |
| - | if(error) |
| - | hypererr = hyper_task_value(task); |
| - | hyper_task_free(task); |
| - | if(error) { |
| - | /* this could probably use a better error code? */ |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| + | case TUNNEL_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. */ |
| + | 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. */ |
| + | infof(data, "Connect me again please"); |
| + | Curl_cfilter_close(data, conn, cf->sockindex); |
| + | result = cf->next->cft->connect(cf->next, data, FALSE, &done); |
| + | goto out; |
| } | |
| - | } while(task); |
| - | s->tunnel_state = TUNNEL_CONNECT; |
| - | /* FALLTHROUGH */ |
| - | case TUNNEL_CONNECT: { |
| - | int didwhat; |
| - | bool done = FALSE; |
| - | result = Curl_hyper_stream(data, conn, &didwhat, &done, |
| - | CURL_CSELECT_IN | CURL_CSELECT_OUT); |
| - | if(result) |
| - | goto error; |
| - | if(!done) |
| - | break; |
| - | s->tunnel_state = TUNNEL_COMPLETE; |
| - | if(h->exec) { |
| - | hyper_executor_free(h->exec); |
| - | h->exec = NULL; |
| - | } |
| - | if(h->read_waker) { |
| - | hyper_waker_free(h->read_waker); |
| - | h->read_waker = NULL; |
| + | /* staying on this connection, reset state */ |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_INIT, data); |
| } | |
| - | if(h->write_waker) { |
| - | hyper_waker_free(h->write_waker); |
| - | h->write_waker = NULL; |
| - | } |
| - | } |
| - | break; |
| - | |
| - | default: |
| break; | |
| - | } |
| - | if(conn->bits.close && data->req.newurl) { |
| - | /* Connection closed by server. Don't use it anymore */ |
| - | Curl_closesocket(data, conn, conn->sock[sockindex]); |
| - | conn->sock[sockindex] = CURL_SOCKET_BAD; |
| + | default: |
| break; | |
| } | |
| - | /* If we are supposed to continue and request a new URL, which basically |
| - | * means the HTTP authentication is still going on so if the tunnel |
| - | * is complete we start over in INIT state */ |
| - | if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { |
| - | infof(data, "CONNECT request done, loop to make another"); |
| - | connect_init(data, TRUE); /* reinit */ |
| - | } |
| } while(data->req.newurl); | |
| + | DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE); |
| + | if(data->info.httpproxycode/100 != 2) { |
| + | /* a non-2xx response and we have no next url to try. */ |
| + | free(data->req.newurl); |
| + | data->req.newurl = NULL; |
| + | /* failure, close this connection to avoid re-use */ |
| + | streamclose(conn, "proxy CONNECT failure"); |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_FAILED, data); |
| + | failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); |
| + | return CURLE_RECV_ERROR; |
| + | } |
| + | /* 2xx response, SUCCESS! */ |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_ESTABLISHED, data); |
| + | infof(data, "CONNECT tunnel established, response %d", |
| + | data->info.httpproxycode); |
| result = CURLE_OK; | |
| - | if(s->tunnel_state == TUNNEL_COMPLETE) { |
| - | if(data->info.httpproxycode/100 != 2) { |
| - | if(conn->bits.close && data->req.newurl) { |
| - | conn->bits.proxy_connect_closed = TRUE; |
| - | infof(data, "Connect me again please"); |
| - | Curl_connect_done(data); |
| - | } |
| - | else { |
| - | free(data->req.newurl); |
| - | data->req.newurl = NULL; |
| - | /* failure, close this connection to avoid re-use */ |
| - | streamclose(conn, "proxy CONNECT failure"); |
| - | Curl_closesocket(data, conn, conn->sock[sockindex]); |
| - | conn->sock[sockindex] = CURL_SOCKET_BAD; |
| - | } |
| - | /* to back to init state */ |
| - | s->tunnel_state = TUNNEL_INIT; |
| + | out: |
| + | if(result) |
| + | tunnel_go_state(cf->conn, ts, TUNNEL_FAILED, data); |
| + | return result; |
| + | } |
| - | if(!conn->bits.proxy_connect_closed) { |
| - | failf(data, "Received HTTP code %d from proxy after CONNECT", |
| - | data->req.httpcode); |
| - | result = CURLE_RECV_ERROR; |
| - | } |
| - | } |
| + | static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool blocking, bool *done) |
| + | { |
| + | CURLcode result; |
| + | struct tunnel_state *ts = cf->ctx; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| } | |
| - | error: |
| - | free(host); |
| - | free(hostheader); |
| - | if(io) |
| - | hyper_io_free(io); |
| - | if(options) |
| - | hyper_clientconn_options_free(options); |
| + | result = cf->next->cft->connect(cf->next, data, blocking, done); |
| + | if(result || !*done) |
| + | return result; |
| - | if(handshake) |
| - | hyper_task_free(handshake); |
| + | /* TODO: can we do blocking? */ |
| + | /* We want "seamless" operations through HTTP proxy tunnel */ |
| - | if(hypererr) { |
| - | uint8_t errbuf[256]; |
| - | size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); |
| - | failf(data, "Hyper: %.*s", (int)errlen, errbuf); |
| - | hyper_error_free(hypererr); |
| + | /* for the secondary socket (FTP), use the "connect to host" |
| + | * but ignore the "connect to port" (use the secondary port) |
| + | */ |
| + | *done = FALSE; |
| + | if(!ts) { |
| + | result = tunnel_init(&ts, data, cf->conn, cf->sockindex); |
| + | if(result) |
| + | return result; |
| + | cf->ctx = ts; |
| } | |
| + | |
| + | DEBUGF(infof(data, "CONNECT(%s:%d, state=%d)", |
| + | ts->hostname, ts->remote_port, ts->tunnel_state)); |
| + | result = CONNECT(cf, data, ts); |
| + | if(result) |
| + | goto out; |
| + | Curl_safefree(data->state.aptr.proxyuserpwd); |
| + | |
| + | out: |
| + | *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); |
| + | if (*done) { |
| + | cf->connected = TRUE; |
| + | tunnel_free(cf, data); |
| + | } |
| + | DEBUGF(infof(data, "http_proxy_cf_connect(handle=%p, i=%d, block=%d) " |
| + | "-> %d, done=%d", data, cf->sockindex, blocking, result, *done)); |
| return result; | |
| } | |
| - | #endif |
| - | void Curl_connect_free(struct Curl_easy *data) |
| + | static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks) |
| { | |
| - | struct connectdata *conn = data->conn; |
| - | struct http_connect_state *s = conn->connect_state; |
| - | if(s) { |
| - | free(s); |
| - | conn->connect_state = NULL; |
| + | 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]; |
| + | 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) |
| + | return GETSOCK_WRITESOCK(0); |
| + | return GETSOCK_READSOCK(0); |
| + | } |
| + | return GETSOCK_WRITESOCK(0); |
| } | |
| + | return fds; |
| } | |
| - | /* |
| - | * Curl_proxyCONNECT() requires that we're connected to an HTTP proxy. This |
| - | * function will issue the necessary commands to get a seamless tunnel through |
| - | * this proxy. After that, the socket can be used just as a normal socket. |
| - | */ |
| - | |
| - | CURLcode Curl_proxyCONNECT(struct Curl_easy *data, |
| - | int sockindex, |
| - | const char *hostname, |
| - | int remote_port) |
| + | static void http_proxy_cf_detach_data(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| { | |
| - | CURLcode result; |
| - | struct connectdata *conn = data->conn; |
| - | if(!conn->connect_state) { |
| - | result = connect_init(data, FALSE); |
| - | if(result) |
| - | return result; |
| + | if(cf->ctx) { |
| + | tunnel_free(cf, data); |
| } | |
| - | result = CONNECT(data, sockindex, hostname, remote_port); |
| + | } |
| - | if(result || Curl_connect_complete(conn)) |
| - | Curl_connect_done(data); |
| + | static void http_proxy_cf_destroy(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | http_proxy_cf_detach_data(cf, data); |
| + | } |
| - | return result; |
| + | static void http_proxy_cf_close(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | DEBUGASSERT(cf->next); |
| + | cf->connected = FALSE; |
| + | cf->next->cft->close(cf->next, data); |
| + | if(cf->ctx) { |
| + | tunnel_go_state(cf->conn, cf->ctx, TUNNEL_INIT, data); |
| + | } |
| } | |
| - | #else |
| - | void Curl_connect_free(struct Curl_easy *data) |
| + | |
| + | static const struct Curl_cftype cft_http_proxy = { |
| + | "HTTP-PROXY", |
| + | http_proxy_cf_destroy, |
| + | Curl_cf_def_attach_data, |
| + | http_proxy_cf_detach_data, |
| + | Curl_cf_def_setup, |
| + | http_proxy_cf_close, |
| + | http_proxy_cf_connect, |
| + | http_proxy_cf_get_select_socks, |
| + | Curl_cf_def_data_pending, |
| + | Curl_cf_def_send, |
| + | Curl_cf_def_recv, |
| + | }; |
| + | |
| + | CURLcode Curl_cfilter_http_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| { | |
| - | (void)data; |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_http_proxy, NULL); |
| + | if(!result) |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | return result; |
| } | |
| - | #endif /* CURL_DISABLE_PROXY */ |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| @@ -28,54 +28,14 @@ | |
| #include "urldata.h" | |
| #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) | |
| - | /* ftp can use this as well */ |
| - | CURLcode Curl_proxyCONNECT(struct Curl_easy *data, |
| - | int tunnelsocket, |
| - | const char *hostname, int remote_port); |
| /* Default proxy timeout in milliseconds */ | |
| #define PROXY_TIMEOUT (3600*1000) | |
| - | CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex); |
| + | CURLcode Curl_cfilter_http_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| - | bool Curl_connect_complete(struct connectdata *conn); |
| - | bool Curl_connect_ongoing(struct connectdata *conn); |
| - | int Curl_connect_getsock(struct connectdata *conn); |
| - | void Curl_connect_done(struct Curl_easy *data); |
| - | |
| - | #else |
| - | #define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN |
| - | #define Curl_proxy_connect(x,y) CURLE_OK |
| - | #define Curl_connect_complete(x) CURLE_OK |
| - | #define Curl_connect_ongoing(x) FALSE |
| - | #define Curl_connect_getsock(x) 0 |
| - | #define Curl_connect_done(x) |
| #endif | |
| - | void Curl_connect_free(struct Curl_easy *data); |
| - | |
| - | /* struct for HTTP CONNECT state data */ |
| - | struct http_connect_state { |
| - | struct HTTP http_proxy; |
| - | struct HTTP *prot_save; |
| - | struct dynbuf rcvbuf; |
| - | struct dynbuf req; |
| - | size_t nsend; |
| - | size_t headerlines; |
| - | enum keeponval { |
| - | KEEPON_DONE, |
| - | KEEPON_CONNECT, |
| - | KEEPON_IGNORE |
| - | } keepon; |
| - | curl_off_t cl; /* size of content to read and ignore */ |
| - | enum { |
| - | TUNNEL_INIT, /* init/default/no tunnel state */ |
| - | TUNNEL_CONNECT, /* CONNECT has been sent off */ |
| - | TUNNEL_COMPLETE, /* CONNECT response received completely */ |
| - | TUNNEL_EXIT |
| - | } tunnel_state; |
| - | BIT(chunked_encoding); |
| - | BIT(close_connection); |
| - | }; |
| - | |
| #endif /* HEADER_CURL_HTTP_PROXY_H */ | |
| @@ -75,6 +75,7 @@ | |
| #include "strtoofft.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "select.h" | |
| #include "multiif.h" | |
| @@ -478,9 +479,16 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, | |
| { | |
| /* Start the SSL connection */ | |
| struct imap_conn *imapc = &conn->proto.imapc; | |
| - | CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &imapc->ssldone); |
| + | CURLcode result; |
| + | if(!Curl_cfilter_ssl_added(data, conn, FIRSTSOCKET)) { |
| + | result = Curl_cfilter_ssl_add(data, conn, FIRSTSOCKET); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &imapc->ssldone); |
| if(!result) { | |
| if(imapc->state != IMAP_UPGRADETLS) | |
| state(data, IMAP_UPGRADETLS); | |
| @@ -490,7 +498,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, | |
| result = imap_perform_capability(data, conn); | |
| } | |
| } | |
| - | |
| + | out: |
| return result; | |
| } | |
| @@ -950,7 +958,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, | |
| line += wordlen; | |
| } | |
| } | |
| - | else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { |
| + | else if(data->set.use_ssl && !Curl_ssl_use(conn, FIRSTSOCKET)) { |
| /* PREAUTH is not compatible with STARTTLS. */ | |
| if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { | |
| /* Switch to TLS connection now */ | |
| @@ -1384,8 +1392,8 @@ 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_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &imapc->ssldone); |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &imapc->ssldone); |
| if(result || !imapc->ssldone) | |
| return result; | |
| } | |
| @@ -1602,7 +1610,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, | |
| /* Run the state-machine */ | |
| result = imap_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, conn, FIRSTSOCKET); |
| if(*dophase_done) | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -29,6 +29,7 @@ | |
| #include "urldata.h" | |
| #include "transfer.h" | |
| #include "url.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "progress.h" | |
| #include "easyif.h" | |
| @@ -160,7 +161,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state | |
| NULL, /* TUNNELING */ | |
| NULL, /* PROTOCONNECT */ | |
| NULL, /* PROTOCONNECTING */ | |
| - | Curl_connect_free, /* DO */ |
| + | NULL, /* DO */ |
| NULL, /* DOING */ | |
| NULL, /* DOING_MORE */ | |
| before_perform, /* DID */ | |
| @@ -709,6 +710,11 @@ static CURLcode multi_done(struct Curl_easy *data, | |
| #endif | |
| ) || conn->bits.close | |
| || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { | |
| + | DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d" |
| + | ", close=%d, premature=%d, stream=%d", |
| + | conn->connection_id, |
| + | data->set.reuse_forbid, conn->bits.close, premature, |
| + | (conn->handler->flags & PROTOPT_STREAM))); |
| connclose(conn, "disconnecting"); | |
| Curl_conncache_remove_conn(data, conn, FALSE); | |
| CONNCACHE_UNLOCK(data); | |
| @@ -948,9 +954,8 @@ void Curl_detach_connection(struct Curl_easy *data) | |
| { | |
| struct connectdata *conn = data->conn; | |
| if(conn) { | |
| - | Curl_connect_done(data); /* if mid-CONNECT, shut it down */ |
| + | Curl_cfilter_detach_data(conn, data); |
| Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); | |
| - | Curl_ssl_detach_conn(data, conn); |
| } | |
| data->conn = NULL; | |
| } | |
| @@ -968,53 +973,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_cfilter_attach_data(conn, data); |
| if(conn->handler->attach) | |
| conn->handler->attach(data, conn); | |
| - | Curl_ssl_associate_conn(data, conn); |
| - | } |
| - | |
| - | static int waitconnect_getsock(struct connectdata *conn, |
| - | curl_socket_t *sock) |
| - | { |
| - | int i; |
| - | int s = 0; |
| - | int rc = 0; |
| - | |
| - | #ifdef USE_SSL |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(CONNECT_FIRSTSOCKET_PROXY_SSL()) |
| - | return Curl_ssl->getsock(conn, sock); |
| - | #endif |
| - | #endif |
| - | |
| - | if(SOCKS_STATE(conn->cnnct.state)) |
| - | return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); |
| - | |
| - | for(i = 0; i<2; i++) { |
| - | if(conn->tempsock[i] != CURL_SOCKET_BAD) { |
| - | sock[s] = conn->tempsock[i]; |
| - | rc |= GETSOCK_WRITESOCK(s); |
| - | #ifdef ENABLE_QUIC |
| - | if(conn->transport == TRNSPRT_QUIC) |
| - | /* when connecting QUIC, we want to read the socket too */ |
| - | rc |= GETSOCK_READSOCK(s); |
| - | #endif |
| - | s++; |
| - | } |
| - | } |
| - | |
| - | return rc; |
| - | } |
| - | |
| - | static int waitproxyconnect_getsock(struct connectdata *conn, |
| - | curl_socket_t *sock) |
| - | { |
| - | sock[0] = conn->sock[FIRSTSOCKET]; |
| - | |
| - | if(conn->connect_state) |
| - | return Curl_connect_getsock(conn); |
| - | |
| - | return GETSOCK_WRITESOCK(0); |
| } | |
| static int domore_getsock(struct Curl_easy *data, | |
| @@ -1076,10 +1037,8 @@ static int multi_getsock(struct Curl_easy *data, | |
| return doing_getsock(data, conn, socks); | |
| case MSTATE_TUNNELING: | |
| - | return waitproxyconnect_getsock(conn, socks); |
| - | |
| case MSTATE_CONNECTING: | |
| - | return waitconnect_getsock(conn, socks); |
| + | return Curl_cfilter_get_select_socks(data, conn, FIRSTSOCKET, socks); |
| case MSTATE_DOING_MORE: | |
| return domore_getsock(data, conn, socks); | |
| @@ -1737,7 +1696,8 @@ static CURLcode protocol_connect(struct Curl_easy *data, | |
| *protocol_done = FALSE; | |
| - | if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { |
| + | if(Curl_cfilter_is_connected(data, conn, FIRSTSOCKET) |
| + | && conn->bits.protoconnstart) { |
| /* We already are connected, get back. This may happen when the connect | |
| worked fine in the first call, like when we connect to a local server | |
| or proxy. Note that we don't know if the protocol is actually done. | |
| @@ -1751,21 +1711,6 @@ static CURLcode protocol_connect(struct Curl_easy *data, | |
| } | |
| if(!conn->bits.protoconnstart) { | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | result = Curl_proxy_connect(data, FIRSTSOCKET); |
| - | if(result) |
| - | return result; |
| - | |
| - | if(CONNECT_FIRSTSOCKET_PROXY_SSL()) |
| - | /* wait for HTTPS proxy SSL initialization to complete */ |
| - | return CURLE_OK; |
| - | |
| - | if(conn->bits.tunnel_proxy && conn->bits.httpproxy && |
| - | Curl_connect_ongoing(conn)) |
| - | /* when using an HTTP tunnel proxy, await complete tunnel establishment |
| - | before proceeding further. Return CURLE_OK so we'll be called again */ |
| - | return CURLE_OK; |
| - | #endif |
| if(conn->handler->connect_it) { | |
| /* is there a protocol-specific connect() procedure? */ | |
| @@ -1901,7 +1846,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| if(data->set.connecttimeout) | |
| Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); | |
| - | result = Curl_connect(data, &async, &protocol_connected); |
| + | result = Curl_connect(data, &async, &connected); |
| if(CURLE_NO_CONNECTION_AVAILABLE == result) { | |
| /* There was no connection available. We will go to the pending | |
| state and wait for an available connection. */ | |
| @@ -1929,15 +1874,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| WAITDO or DO! */ | |
| rc = CURLM_CALL_MULTI_PERFORM; | |
| - | if(protocol_connected) |
| - | multistate(data, MSTATE_DO); |
| + | if(connected) |
| + | multistate(data, MSTATE_PROTOCONNECT); |
| else { | |
| - | #ifndef CURL_DISABLE_HTTP |
| - | if(Curl_connect_ongoing(data->conn)) |
| - | multistate(data, MSTATE_TUNNELING); |
| - | else |
| - | #endif |
| - | multistate(data, MSTATE_CONNECTING); |
| + | multistate(data, MSTATE_CONNECTING); |
| } | |
| } | |
| } | |
| @@ -1989,7 +1929,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| if(dns) { | |
| /* Perform the next step in the connection phase, and then move on | |
| to the WAITCONNECT state */ | |
| - | result = Curl_once_resolved(data, &protocol_connected); |
| + | result = Curl_once_resolved(data, &connected); |
| if(result) | |
| /* if Curl_once_resolved() returns failure, the connection struct | |
| @@ -1998,15 +1938,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| else { | |
| /* call again please so that we get the next socket setup */ | |
| rc = CURLM_CALL_MULTI_PERFORM; | |
| - | if(protocol_connected) |
| - | multistate(data, MSTATE_DO); |
| + | if(connected) |
| + | multistate(data, MSTATE_PROTOCONNECT); |
| else { | |
| - | #ifndef CURL_DISABLE_HTTP |
| - | if(Curl_connect_ongoing(data->conn)) |
| - | multistate(data, MSTATE_TUNNELING); |
| - | else |
| - | #endif |
| - | multistate(data, MSTATE_CONNECTING); |
| + | multistate(data, MSTATE_CONNECTING); |
| } | |
| } | |
| } | |
| @@ -2035,16 +1970,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| else | |
| #endif | |
| if(!result) { | |
| - | if( |
| - | #ifndef CURL_DISABLE_PROXY |
| - | (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || |
| - | data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && |
| - | #endif |
| - | Curl_connect_complete(data->conn)) { |
| - | rc = CURLM_CALL_MULTI_PERFORM; |
| - | /* initiate protocol connect phase */ |
| - | multistate(data, MSTATE_PROTOCONNECT); |
| - | } |
| + | rc = CURLM_CALL_MULTI_PERFORM; |
| + | /* initiate protocol connect phase */ |
| + | multistate(data, MSTATE_PROTOCONNECT); |
| } | |
| else | |
| stream_error = TRUE; | |
| @@ -2054,27 +1982,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| case MSTATE_CONNECTING: | |
| /* awaiting a completion of an asynch TCP connect */ | |
| DEBUGASSERT(data->conn); | |
| - | result = Curl_is_connected(data, data->conn, FIRSTSOCKET, &connected); |
| + | result = Curl_cfilter_connect(data, data->conn, FIRSTSOCKET, |
| + | FALSE, &connected); |
| if(connected && !result) { | |
| - | #ifndef CURL_DISABLE_HTTP |
| - | if( |
| - | #ifndef CURL_DISABLE_PROXY |
| - | (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && |
| - | !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || |
| - | #endif |
| - | Curl_connect_ongoing(data->conn)) { |
| - | multistate(data, MSTATE_TUNNELING); |
| - | break; |
| - | } |
| - | #endif |
| rc = CURLM_CALL_MULTI_PERFORM; | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | multistate(data, |
| - | data->conn->bits.tunnel_proxy? |
| - | MSTATE_TUNNELING : MSTATE_PROTOCONNECT); |
| - | #else |
| multistate(data, MSTATE_PROTOCONNECT); | |
| - | #endif |
| } | |
| else if(result) { | |
| /* failure detected */ | |
| @@ -2086,6 +1998,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| break; | |
| case MSTATE_PROTOCONNECT: | |
| + | if(data->conn->bits.reuse) { |
| + | /* ftp seems to hang when protoconnect on reused connection |
| + | * since we handle PROTOCONNECT in general inside the filers, it |
| + | * seems wrong to restart this on a reused connection. */ |
| + | multistate(data, MSTATE_DO); |
| + | rc = CURLM_CALL_MULTI_PERFORM; |
| + | break; |
| + | } |
| result = protocol_connect(data, &protocol_connected); | |
| if(!result && !protocol_connected) | |
| /* switch to waiting state */ | |
| @@ -47,6 +47,7 @@ | |
| #include "transfer.h" | |
| #include "curl_ldap.h" | |
| #include "curl_base64.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "curl_sasl.h" | |
| #include "strcase.h" | |
| @@ -500,8 +501,7 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) | |
| struct ldapconninfo *li = conn->proto.ldapc; | |
| bool ssldone = 0; | |
| - | result = Curl_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &ssldone); |
| + | result = Curl_cfilter_connect(data, FIRSTSOCKET, FALSE, &ssldone); |
| if(!result) { | |
| state(data, newstate); | |
| @@ -1153,7 +1153,7 @@ ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) | |
| (void)arg; | |
| if(opt == LBER_SB_OPT_DATA_READY) { | |
| struct Curl_easy *data = sbiod->sbiod_pvt; | |
| - | return Curl_ssl_data_pending(data->conn, FIRSTSOCKET); |
| + | return Curl_cfilter_data_pending(data->conn, FIRSTSOCKET); |
| } | |
| return 0; | |
| } | |
| @@ -28,6 +28,7 @@ | |
| #include "curl_setup.h" | |
| #include "urldata.h" | |
| + | #include "cfilters.h" |
| #include "sendf.h" | |
| #include "select.h" | |
| #include "progress.h" | |
| @@ -102,12 +103,12 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, | |
| else | |
| interval_ms = 0; /* immediate */ | |
| - | if(Curl_ssl_data_pending(conn, FIRSTSOCKET)) |
| + | if(Curl_cfilter_data_pending(data, conn, FIRSTSOCKET)) |
| rc = 1; | |
| else if(Curl_pp_moredata(pp)) | |
| /* We are receiving and there is data in the cache so just read it */ | |
| rc = 1; | |
| - | else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET)) |
| + | else if(!pp->sendleft && Curl_cfilter_data_pending(data, conn, FIRSTSOCKET)) |
| /* We are receiving and there is data ready in the SSL library */ | |
| rc = 1; | |
| else | |
| @@ -76,6 +76,7 @@ | |
| #include "strtoofft.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "select.h" | |
| #include "multiif.h" | |
| @@ -373,9 +374,16 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, | |
| { | |
| /* Start the SSL connection */ | |
| struct pop3_conn *pop3c = &conn->proto.pop3c; | |
| - | CURLcode result = |
| - | Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, |
| - | &pop3c->ssldone); |
| + | CURLcode result; |
| + | |
| + | if(!Curl_cfilter_ssl_added(data, conn, FIRSTSOCKET)) { |
| + | result = Curl_cfilter_ssl_add(data, conn, FIRSTSOCKET); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &pop3c->ssldone); |
| if(!result) { | |
| if(pop3c->state != POP3_UPGRADETLS) | |
| @@ -386,7 +394,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, | |
| result = pop3_perform_capa(data, conn); | |
| } | |
| } | |
| - | |
| + | out: |
| return result; | |
| } | |
| @@ -767,7 +775,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 || conn->ssl[FIRSTSOCKET].use) |
| + | if(!data->set.use_ssl || Curl_ssl_use(conn, FIRSTSOCKET)) |
| result = pop3_perform_authentication(data, conn); | |
| else if(pop3code == '+' && pop3c->tls_supported) | |
| /* Switch to TLS connection now */ | |
| @@ -1054,8 +1062,8 @@ 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_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &pop3c->ssldone); |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &pop3c->ssldone); |
| if(result || !pop3c->ssldone) | |
| return result; | |
| } | |
| @@ -1192,7 +1200,6 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, | |
| { | |
| /* This is POP3 and no proxy */ | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| struct POP3 *pop3 = data->req.p.pop3; | |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -1211,7 +1218,7 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, | |
| /* Run the state-machine */ | |
| result = pop3_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -27,10 +27,14 @@ | |
| #ifdef HAVE_FCNTL_H | |
| #include <fcntl.h> | |
| #endif | |
| + | #ifdef HAVE_ARPA_INET_H |
| + | #include <arpa/inet.h> |
| + | #endif |
| #include <curl/curl.h> | |
| #include "vtls/vtls.h" | |
| #include "sendf.h" | |
| + | #include "timeval.h" |
| #include "rand.h" | |
| /* The last 3 #include files should be in this order */ | |
| @@ -38,6 +38,7 @@ | |
| #include "urldata.h" | |
| #include "sendf.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "vtls/vtls.h" | |
| #include "vssh/ssh.h" | |
| @@ -159,7 +160,7 @@ static CURLcode pre_receive_plain(struct Curl_easy *data, | |
| 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_recv_plain && |
| + | conn->recv[num] == Curl_cfilter_recv && |
| (!psnd->buffer || bytestorecv)) { | |
| const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD, | |
| CURL_SOCKET_BAD, 0); | |
| @@ -718,11 +719,14 @@ CURLcode Curl_read(struct Curl_easy *data, /* transfer */ | |
| nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result); | |
| if(nread < 0) | |
| - | return result; |
| + | goto out; |
| *n += nread; | |
| - | |
| - | return CURLE_OK; |
| + | result = CURLE_OK; |
| + | out: |
| + | /* DEBUGF(infof(data, "Curl_read(handle=%p) -> %d, nread=%ld", |
| + | data, result, nread)); */ |
| + | return result; |
| } | |
| /* return 0 on success */ | |
| @@ -229,7 +229,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| break; | |
| #endif | |
| case CURLOPT_TLS13_CIPHERS: | |
| - | if(Curl_ssl_tls13_ciphersuites()) { |
| + | if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { |
| /* set preferred list of TLS 1.3 cipher suites */ | |
| result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST], | |
| va_arg(param, char *)); | |
| @@ -239,7 +239,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| break; | |
| #ifndef CURL_DISABLE_PROXY | |
| case CURLOPT_PROXY_TLS13_CIPHERS: | |
| - | if(Curl_ssl_tls13_ciphersuites()) { |
| + | if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { |
| /* set preferred list of TLS 1.3 cipher suites for proxy */ | |
| result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY], | |
| va_arg(param, char *)); | |
| @@ -2001,7 +2001,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Set a SSL_CTX callback | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_SSL_CTX) |
| + | if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) |
| data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); | |
| else | |
| #endif | |
| @@ -2012,7 +2012,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Set a SSL_CTX callback parameter pointer | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_SSL_CTX) |
| + | if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) |
| data->set.ssl.fsslctxp = va_arg(param, void *); | |
| else | |
| #endif | |
| @@ -2022,7 +2022,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| /* | |
| * Enable TLS false start. | |
| */ | |
| - | if(!Curl_ssl_false_start()) { |
| + | if(!Curl_ssl_false_start(data)) { |
| result = CURLE_NOT_BUILT_IN; | |
| break; | |
| } | |
| @@ -2031,7 +2031,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| break; | |
| case CURLOPT_CERTINFO: | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_CERTINFO) |
| + | if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) |
| data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; | |
| else | |
| #endif | |
| @@ -2043,7 +2043,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Specify file name of the public key in DER format. | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) |
| + | if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) |
| result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], | |
| va_arg(param, char *)); | |
| else | |
| @@ -2057,7 +2057,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Specify file name of the public key in DER format. | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) |
| + | if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) |
| result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], | |
| va_arg(param, char *)); | |
| else | |
| @@ -2078,7 +2078,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Specify entire PEM of the CA certificate | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) |
| + | if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) |
| result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], | |
| va_arg(param, struct curl_blob *)); | |
| else | |
| @@ -2101,7 +2101,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * Specify entire PEM of the CA certificate | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) |
| + | if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) |
| result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], | |
| va_arg(param, struct curl_blob *)); | |
| else | |
| @@ -2115,7 +2115,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * certificates which have been prepared using openssl c_rehash utility. | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_CA_PATH) |
| + | if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) |
| /* This does not work on windows. */ | |
| result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], | |
| va_arg(param, char *)); | |
| @@ -2130,7 +2130,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) | |
| * CA certificates which have been prepared using openssl c_rehash utility. | |
| */ | |
| #ifdef USE_SSL | |
| - | if(Curl_ssl->supports & SSLSUPP_CA_PATH) |
| + | if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) |
| /* This does not work on windows. */ | |
| result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], | |
| va_arg(param, char *)); | |
| @@ -38,6 +38,7 @@ | |
| #include "urldata.h" | |
| #include "sendf.h" | |
| #include "multiif.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "progress.h" | |
| #include "transfer.h" | |
| @@ -666,8 +667,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) | |
| #ifdef USE_SSL | |
| if((conn->handler->flags & PROTOPT_SSL)) { | |
| bool ssl_done = FALSE; | |
| - | result = Curl_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &ssl_done); |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &ssl_done); |
| if(result && result != CURLE_AGAIN) | |
| return result; | |
| if(!ssl_done) | |
| @@ -79,6 +79,7 @@ | |
| #include "strtoofft.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "select.h" | |
| #include "multiif.h" | |
| @@ -400,10 +401,16 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) | |
| /* Start the SSL connection */ | |
| struct connectdata *conn = data->conn; | |
| struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| - | CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, |
| - | &smtpc->ssldone); |
| + | CURLcode result; |
| + | |
| + | if(!Curl_cfilter_ssl_added(data, conn, FIRSTSOCKET)) { |
| + | result = Curl_cfilter_ssl_add(data, conn, FIRSTSOCKET); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &smtpc->ssldone); |
| if(!result) { | |
| if(smtpc->state != SMTP_UPGRADETLS) | |
| state(data, SMTP_UPGRADETLS); | |
| @@ -413,7 +420,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) | |
| result = smtp_perform_ehlo(data); | |
| } | |
| } | |
| - | |
| + | out: |
| return result; | |
| } | |
| @@ -888,7 +895,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, | |
| (void)instate; /* no use for this yet */ | |
| if(smtpcode/100 != 2 && smtpcode != 1) { | |
| - | if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) |
| + | if(data->set.use_ssl <= CURLUSESSL_TRY || Curl_ssl_use(conn, FIRSTSOCKET)) |
| result = smtp_perform_helo(data, conn); | |
| else { | |
| failf(data, "Remote access denied: %d", smtpcode); | |
| @@ -953,7 +960,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, | |
| } | |
| if(smtpcode != 1) { | |
| - | if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { |
| + | if(data->set.use_ssl && !Curl_ssl_use(conn, FIRSTSOCKET)) { |
| /* We don't have a SSL/TLS connection yet, but SSL is requested */ | |
| if(smtpc->tls_supported) | |
| /* Switch to TLS connection now */ | |
| @@ -1285,8 +1292,8 @@ 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_ssl_connect_nonblocking(data, conn, FALSE, |
| - | FIRSTSOCKET, &smtpc->ssldone); |
| + | result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, |
| + | FALSE, &smtpc->ssldone); |
| if(result || !smtpc->ssldone) | |
| return result; | |
| } | |
| @@ -1479,7 +1486,6 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, | |
| { | |
| /* This is SMTP and no proxy */ | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| struct SMTP *smtp = data->req.p.smtp; | |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -1519,7 +1525,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, | |
| /* Run the state-machine */ | |
| result = smtp_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -36,17 +36,52 @@ | |
| #include "urldata.h" | |
| #include "sendf.h" | |
| #include "select.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "timeval.h" | |
| #include "socks.h" | |
| #include "multiif.h" /* for getsock macros */ | |
| #include "inet_pton.h" | |
| + | #include "url.h" |
| /* The last 3 #include files should be in this order */ | |
| #include "curl_printf.h" | |
| #include "curl_memory.h" | |
| #include "memdebug.h" | |
| + | /* for the (SOCKS) connect state machine */ |
| + | enum connect_t { |
| + | CONNECT_INIT, |
| + | CONNECT_SOCKS_INIT, /* 1 */ |
| + | CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ |
| + | CONNECT_SOCKS_READ_INIT, /* 3 set up read */ |
| + | CONNECT_SOCKS_READ, /* 4 read server response */ |
| + | CONNECT_GSSAPI_INIT, /* 5 */ |
| + | CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ |
| + | CONNECT_AUTH_SEND, /* 7 send auth */ |
| + | CONNECT_AUTH_READ, /* 8 read auth response */ |
| + | CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ |
| + | CONNECT_RESOLVING, /* 10 */ |
| + | CONNECT_RESOLVED, /* 11 */ |
| + | CONNECT_RESOLVE_REMOTE, /* 12 */ |
| + | CONNECT_REQ_SEND, /* 13 */ |
| + | CONNECT_REQ_SENDING, /* 14 */ |
| + | CONNECT_REQ_READ, /* 15 */ |
| + | CONNECT_REQ_READ_MORE, /* 16 */ |
| + | CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ |
| + | }; |
| + | |
| + | struct socks_state { |
| + | enum connect_t state; |
| + | ssize_t outstanding; /* send this many bytes more */ |
| + | unsigned char *outp; /* send from this pointer */ |
| + | |
| + | const char *hostname; |
| + | int remote_port; |
| + | const char *proxy_user; |
| + | const char *proxy_password; |
| + | }; |
| + | |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| /* | |
| * Helper read-from-socket functions. Does the same as Curl_read() but it | |
| @@ -104,21 +139,20 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ | |
| #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| #define DEBUG_AND_VERBOSE | |
| - | #define sxstate(x,y) socksstate(x,y, __LINE__) |
| + | #define sxstate(x,d,y) socksstate(x,d,y, __LINE__) |
| #else | |
| - | #define sxstate(x,y) socksstate(x,y) |
| + | #define sxstate(x,d,y) socksstate(x,d,y) |
| #endif | |
| /* always use this function to change state, to make debugging easier */ | |
| - | static void socksstate(struct Curl_easy *data, |
| + | static void socksstate(struct socks_state *sx, struct Curl_easy *data, |
| enum connect_t state | |
| #ifdef DEBUG_AND_VERBOSE | |
| , int lineno | |
| #endif | |
| ) | |
| { | |
| - | struct connectdata *conn = data->conn; |
| - | enum connect_t oldstate = conn->cnnct.state; |
| + | enum connect_t oldstate = sx->state; |
| #ifdef DEBUG_AND_VERBOSE | |
| /* synced with the state list in urldata.h */ | |
| static const char * const statename[] = { | |
| @@ -143,40 +177,21 @@ static void socksstate(struct Curl_easy *data, | |
| }; | |
| #endif | |
| + | (void)data; |
| if(oldstate == state) | |
| /* don't bother when the new state is the same as the old state */ | |
| return; | |
| - | conn->cnnct.state = state; |
| + | sx->state = state; |
| #ifdef DEBUG_AND_VERBOSE | |
| infof(data, | |
| - | "SXSTATE: %s => %s conn %p; line %d", |
| - | statename[oldstate], statename[conn->cnnct.state], conn, |
| + | "SXSTATE: %s => %s; line %d", |
| + | statename[oldstate], statename[sx->state], |
| lineno); | |
| #endif | |
| } | |
| - | int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, |
| - | int sockindex) |
| - | { |
| - | int rc = 0; |
| - | sock[0] = conn->sock[sockindex]; |
| - | switch(conn->cnnct.state) { |
| - | case CONNECT_RESOLVING: |
| - | case CONNECT_SOCKS_READ: |
| - | case CONNECT_AUTH_READ: |
| - | case CONNECT_REQ_READ: |
| - | case CONNECT_REQ_READ_MORE: |
| - | rc = GETSOCK_READSOCK(0); |
| - | break; |
| - | default: |
| - | rc = GETSOCK_WRITESOCK(0); |
| - | break; |
| - | } |
| - | return rc; |
| - | } |
| - | |
| /* | |
| * This function logs in to a SOCKS4 proxy and sends the specifics to the final | |
| * destination server. | |
| @@ -188,20 +203,16 @@ int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, | |
| * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" | |
| * Nonsupport "Identification Protocol (RFC1413)" | |
| */ | |
| - | CURLproxycode Curl_SOCKS4(const char *proxy_user, |
| - | const char *hostname, |
| - | int remote_port, |
| - | int sockindex, |
| - | struct Curl_easy *data, |
| - | bool *done) |
| + | static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, |
| + | struct socks_state *sx, |
| + | struct Curl_easy *data) |
| { | |
| - | struct connectdata *conn = data->conn; |
| + | struct connectdata *conn = cf->conn; |
| const bool protocol4a = | |
| (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; | |
| unsigned char *socksreq = (unsigned char *)data->state.buffer; | |
| CURLcode result; | |
| - | curl_socket_t sockfd = conn->sock[sockindex]; |
| - | struct connstate *sx = &conn->cnnct; |
| + | curl_socket_t sockfd = conn->sock[cf->sockindex]; |
| struct Curl_dns_entry *dns = NULL; | |
| ssize_t actualread; | |
| ssize_t written; | |
| @@ -209,18 +220,16 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| /* make sure that the buffer is at least 600 bytes */ | |
| DEBUGASSERT(READBUFFER_MIN >= 600); | |
| - | if(!SOCKS_STATE(sx->state) && !*done) |
| - | sxstate(data, CONNECT_SOCKS_INIT); |
| - | |
| switch(sx->state) { | |
| case CONNECT_SOCKS_INIT: | |
| /* SOCKS4 can only do IPv4, insist! */ | |
| conn->ip_version = CURL_IPRESOLVE_V4; | |
| if(conn->bits.httpproxy) | |
| infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d", | |
| - | protocol4a ? "a" : "", hostname, remote_port); |
| + | protocol4a ? "a" : "", sx->hostname, sx->remote_port); |
| - | infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port); |
| + | infof(data, "SOCKS4 communication to %s:%d", |
| + | sx->hostname, sx->remote_port); |
| /* | |
| * Compose socks4 request | |
| @@ -235,40 +244,40 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| socksreq[0] = 4; /* version (SOCKS4) */ | |
| socksreq[1] = 1; /* connect */ | |
| - | socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ |
| - | socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ |
| + | socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */ |
| + | socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */ |
| /* DNS resolve only for SOCKS4, not SOCKS4a */ | |
| if(!protocol4a) { | |
| enum resolve_t rc = | |
| - | Curl_resolv(data, hostname, remote_port, FALSE, &dns); |
| + | Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns); |
| if(rc == CURLRESOLV_ERROR) | |
| return CURLPX_RESOLVE_HOST; | |
| else if(rc == CURLRESOLV_PENDING) { | |
| - | sxstate(data, CONNECT_RESOLVING); |
| - | infof(data, "SOCKS4 non-blocking resolve of %s", hostname); |
| + | sxstate(sx, data, CONNECT_RESOLVING); |
| + | infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname); |
| return CURLPX_OK; | |
| } | |
| - | sxstate(data, CONNECT_RESOLVED); |
| + | sxstate(sx, data, CONNECT_RESOLVED); |
| goto CONNECT_RESOLVED; | |
| } | |
| /* socks4a doesn't resolve anything locally */ | |
| - | sxstate(data, CONNECT_REQ_INIT); |
| + | sxstate(sx, data, CONNECT_REQ_INIT); |
| goto CONNECT_REQ_INIT; | |
| case CONNECT_RESOLVING: | |
| /* check if we have the name resolved by now */ | |
| - | dns = Curl_fetch_addr(data, hostname, (int)conn->port); |
| + | dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port); |
| if(dns) { | |
| #ifdef CURLRES_ASYNCH | |
| data->state.async.dns = dns; | |
| data->state.async.done = TRUE; | |
| #endif | |
| - | infof(data, "Hostname '%s' was found", hostname); |
| - | sxstate(data, CONNECT_RESOLVED); |
| + | infof(data, "Hostname '%s' was found", sx->hostname); |
| + | sxstate(sx, data, CONNECT_RESOLVED); |
| } | |
| else { | |
| result = Curl_resolv_check(data, &dns); | |
| @@ -309,11 +318,11 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| Curl_resolv_unlock(data, dns); /* not used anymore from now on */ | |
| } | |
| else | |
| - | failf(data, "SOCKS4 connection to %s not supported", hostname); |
| + | failf(data, "SOCKS4 connection to %s not supported", sx->hostname); |
| } | |
| else | |
| failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", | |
| - | hostname); |
| + | sx->hostname); |
| if(!hp) | |
| return CURLPX_RESOLVE_HOST; | |
| @@ -325,14 +334,14 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| * This is currently not supporting "Identification Protocol (RFC1413)". | |
| */ | |
| socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ | |
| - | if(proxy_user) { |
| - | size_t plen = strlen(proxy_user); |
| + | if(sx->proxy_user) { |
| + | size_t plen = strlen(sx->proxy_user); |
| if(plen >= (size_t)data->set.buffer_size - 8) { | |
| failf(data, "Too long SOCKS proxy user name, can't use"); | |
| return CURLPX_LONG_USER; | |
| } | |
| /* copy the proxy name WITH trailing zero */ | |
| - | memcpy(socksreq + 8, proxy_user, plen + 1); |
| + | memcpy(socksreq + 8, sx->proxy_user, plen + 1); |
| } | |
| /* | |
| @@ -350,9 +359,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| socksreq[6] = 0; | |
| socksreq[7] = 1; | |
| /* append hostname */ | |
| - | hostnamelen = strlen(hostname) + 1; /* length including NUL */ |
| + | hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ |
| if(hostnamelen <= 255) | |
| - | strcpy((char *)socksreq + packetsize, hostname); |
| + | strcpy((char *)socksreq + packetsize, sx->hostname); |
| else { | |
| failf(data, "SOCKS4: too long host name"); | |
| return CURLPX_LONG_HOSTNAME; | |
| @@ -361,7 +370,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| } | |
| sx->outp = socksreq; | |
| sx->outstanding = packetsize; | |
| - | sxstate(data, CONNECT_REQ_SENDING); |
| + | sxstate(sx, data, CONNECT_REQ_SENDING); |
| } | |
| /* FALLTHROUGH */ | |
| case CONNECT_REQ_SENDING: | |
| @@ -382,7 +391,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| /* done sending! */ | |
| sx->outstanding = 8; /* receive data size */ | |
| sx->outp = socksreq; | |
| - | sxstate(data, CONNECT_SOCKS_READ); |
| + | sxstate(sx, data, CONNECT_SOCKS_READ); |
| /* FALLTHROUGH */ | |
| case CONNECT_SOCKS_READ: | |
| @@ -405,7 +414,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| sx->outp += actualread; | |
| return CURLPX_OK; | |
| } | |
| - | sxstate(data, CONNECT_DONE); |
| + | sxstate(sx, data, CONNECT_DONE); |
| break; | |
| default: /* lots of unused states in SOCKS4 */ | |
| break; | |
| @@ -478,7 +487,6 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| return CURLPX_UNKNOWN_FAIL; | |
| } | |
| - | *done = TRUE; |
| return CURLPX_OK; /* Proxy was successful! */ | |
| } | |
| @@ -486,13 +494,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, | |
| * This function logs in to a SOCKS5 proxy and sends the specifics to the final | |
| * destination server. | |
| */ | |
| - | CURLproxycode Curl_SOCKS5(const char *proxy_user, |
| - | const char *proxy_password, |
| - | const char *hostname, |
| - | int remote_port, |
| - | int sockindex, |
| - | struct Curl_easy *data, |
| - | bool *done) |
| + | static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, |
| + | struct socks_state *sx, |
| + | struct Curl_easy *data) |
| { | |
| /* | |
| According to the RFC1928, section "6. Replies". This is what a SOCK5 | |
| @@ -510,31 +514,27 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| o REP Reply field: | |
| o X'00' succeeded | |
| */ | |
| - | struct connectdata *conn = data->conn; |
| + | struct connectdata *conn = cf->conn; |
| 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[sockindex]; |
| + | curl_socket_t sockfd = conn->sock[cf->sockindex]; |
| bool socks5_resolve_local = | |
| (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; | |
| - | const size_t hostname_len = strlen(hostname); |
| + | const size_t hostname_len = strlen(sx->hostname); |
| ssize_t len = 0; | |
| const unsigned long auth = data->set.socks5auth; | |
| bool allow_gssapi = FALSE; | |
| - | struct connstate *sx = &conn->cnnct; |
| struct Curl_dns_entry *dns = NULL; | |
| - | if(!SOCKS_STATE(sx->state) && !*done) |
| - | sxstate(data, CONNECT_SOCKS_INIT); |
| - | |
| switch(sx->state) { | |
| case CONNECT_SOCKS_INIT: | |
| if(conn->bits.httpproxy) | |
| infof(data, "SOCKS5: connecting to HTTP proxy %s port %d", | |
| - | hostname, remote_port); |
| + | sx->hostname, sx->remote_port); |
| /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ | |
| if(!socks5_resolve_local && hostname_len > 255) { | |
| @@ -549,7 +549,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| auth); | |
| if(!(auth & CURLAUTH_BASIC)) | |
| /* disable username/password auth */ | |
| - | proxy_user = NULL; |
| + | sx->proxy_user = NULL; |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| if(auth & CURLAUTH_GSSAPI) | |
| allow_gssapi = TRUE; | |
| @@ -561,7 +561,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| socksreq[idx++] = 0; /* no authentication */ | |
| if(allow_gssapi) | |
| socksreq[idx++] = 1; /* GSS-API */ | |
| - | if(proxy_user) |
| + | if(sx->proxy_user) |
| socksreq[idx++] = 2; /* username/password */ | |
| /* write the number of authentication methods */ | |
| socksreq[1] = (unsigned char) (idx - 2); | |
| @@ -572,12 +572,12 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| return CURLPX_SEND_CONNECT; | |
| } | |
| if(written != idx) { | |
| - | sxstate(data, CONNECT_SOCKS_SEND); |
| + | sxstate(sx, data, CONNECT_SOCKS_SEND); |
| sx->outstanding = idx - written; | |
| sx->outp = &socksreq[written]; | |
| return CURLPX_OK; | |
| } | |
| - | sxstate(data, CONNECT_SOCKS_READ); |
| + | sxstate(sx, data, CONNECT_SOCKS_READ); |
| goto CONNECT_SOCKS_READ_INIT; | |
| case CONNECT_SOCKS_SEND: | |
| result = Curl_write_plain(data, sockfd, (char *)sx->outp, | |
| @@ -622,18 +622,18 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| } | |
| else if(socksreq[1] == 0) { | |
| /* DONE! No authentication needed. Send request. */ | |
| - | sxstate(data, CONNECT_REQ_INIT); |
| + | sxstate(sx, data, CONNECT_REQ_INIT); |
| goto CONNECT_REQ_INIT; | |
| } | |
| else if(socksreq[1] == 2) { | |
| /* regular name + password authentication */ | |
| - | sxstate(data, CONNECT_AUTH_INIT); |
| + | sxstate(sx, data, CONNECT_AUTH_INIT); |
| goto CONNECT_AUTH_INIT; | |
| } | |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| else if(allow_gssapi && (socksreq[1] == 1)) { | |
| - | sxstate(data, CONNECT_GSSAPI_INIT); |
| - | result = Curl_SOCKS5_gssapi_negotiate(sockindex, data); |
| + | sxstate(sx, data, CONNECT_GSSAPI_INIT); |
| + | result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data); |
| if(result) { | |
| failf(data, "Unable to negotiate SOCKS5 GSS-API context."); | |
| return CURLPX_GSSAPI; | |
| @@ -668,9 +668,9 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| case CONNECT_AUTH_INIT: { | |
| /* Needs user name and password */ | |
| size_t proxy_user_len, proxy_password_len; | |
| - | if(proxy_user && proxy_password) { |
| - | proxy_user_len = strlen(proxy_user); |
| - | proxy_password_len = strlen(proxy_password); |
| + | if(sx->proxy_user && sx->proxy_password) { |
| + | proxy_user_len = strlen(sx->proxy_user); |
| + | proxy_password_len = strlen(sx->proxy_password); |
| } | |
| else { | |
| proxy_user_len = 0; | |
| @@ -687,26 +687,26 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| len = 0; | |
| socksreq[len++] = 1; /* username/pw subnegotiation version */ | |
| socksreq[len++] = (unsigned char) proxy_user_len; | |
| - | if(proxy_user && proxy_user_len) { |
| + | if(sx->proxy_user && proxy_user_len) { |
| /* the length must fit in a single byte */ | |
| if(proxy_user_len >= 255) { | |
| failf(data, "Excessive user name length for proxy auth"); | |
| return CURLPX_LONG_USER; | |
| } | |
| - | memcpy(socksreq + len, proxy_user, proxy_user_len); |
| + | memcpy(socksreq + len, sx->proxy_user, proxy_user_len); |
| } | |
| len += proxy_user_len; | |
| socksreq[len++] = (unsigned char) proxy_password_len; | |
| - | if(proxy_password && proxy_password_len) { |
| + | if(sx->proxy_password && proxy_password_len) { |
| /* the length must fit in a single byte */ | |
| if(proxy_password_len > 255) { | |
| failf(data, "Excessive password length for proxy auth"); | |
| return CURLPX_LONG_PASSWD; | |
| } | |
| - | memcpy(socksreq + len, proxy_password, proxy_password_len); |
| + | memcpy(socksreq + len, sx->proxy_password, proxy_password_len); |
| } | |
| len += proxy_password_len; | |
| - | sxstate(data, CONNECT_AUTH_SEND); |
| + | sxstate(sx, data, CONNECT_AUTH_SEND); |
| sx->outstanding = len; | |
| sx->outp = socksreq; | |
| } | |
| @@ -726,7 +726,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| } | |
| sx->outp = socksreq; | |
| sx->outstanding = 2; | |
| - | sxstate(data, CONNECT_AUTH_READ); |
| + | sxstate(sx, data, CONNECT_AUTH_READ); |
| /* FALLTHROUGH */ | |
| case CONNECT_AUTH_READ: | |
| result = Curl_read_plain(sockfd, (char *)sx->outp, | |
| @@ -754,36 +754,36 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| } | |
| /* Everything is good so far, user was authenticated! */ | |
| - | sxstate(data, CONNECT_REQ_INIT); |
| + | sxstate(sx, data, CONNECT_REQ_INIT); |
| /* FALLTHROUGH */ | |
| CONNECT_REQ_INIT: | |
| case CONNECT_REQ_INIT: | |
| if(socks5_resolve_local) { | |
| - | enum resolve_t rc = Curl_resolv(data, hostname, remote_port, |
| + | enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port, |
| FALSE, &dns); | |
| if(rc == CURLRESOLV_ERROR) | |
| return CURLPX_RESOLVE_HOST; | |
| if(rc == CURLRESOLV_PENDING) { | |
| - | sxstate(data, CONNECT_RESOLVING); |
| + | sxstate(sx, data, CONNECT_RESOLVING); |
| return CURLPX_OK; | |
| } | |
| - | sxstate(data, CONNECT_RESOLVED); |
| + | sxstate(sx, data, CONNECT_RESOLVED); |
| goto CONNECT_RESOLVED; | |
| } | |
| goto CONNECT_RESOLVE_REMOTE; | |
| case CONNECT_RESOLVING: | |
| /* check if we have the name resolved by now */ | |
| - | dns = Curl_fetch_addr(data, hostname, remote_port); |
| + | dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port); |
| if(dns) { | |
| #ifdef CURLRES_ASYNCH | |
| data->state.async.dns = dns; | |
| data->state.async.done = TRUE; | |
| #endif | |
| - | infof(data, "SOCKS5: hostname '%s' found", hostname); |
| + | infof(data, "SOCKS5: hostname '%s' found", sx->hostname); |
| } | |
| if(!dns) { | |
| @@ -803,13 +803,13 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| hp = dns->addr; | |
| if(!hp) { | |
| failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", | |
| - | hostname); |
| + | sx->hostname); |
| return CURLPX_RESOLVE_HOST; | |
| } | |
| Curl_printable_address(hp, dest, sizeof(dest)); | |
| destlen = strlen(dest); | |
| - | msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port); |
| + | msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port); |
| len = 0; | |
| socksreq[len++] = 5; /* version (SOCKS5) */ | |
| @@ -866,7 +866,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| #ifdef ENABLE_IPV6 | |
| if(conn->bits.ipv6_ip) { | |
| char ip6[16]; | |
| - | if(1 != Curl_inet_pton(AF_INET6, hostname, ip6)) |
| + | if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6)) |
| return CURLPX_BAD_ADDRESS_TYPE; | |
| socksreq[len++] = 4; | |
| memcpy(&socksreq[len], ip6, sizeof(ip6)); | |
| @@ -874,7 +874,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| } | |
| else | |
| #endif | |
| - | if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) { |
| + | if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) { |
| socksreq[len++] = 1; | |
| memcpy(&socksreq[len], ip4, sizeof(ip4)); | |
| len += sizeof(ip4); | |
| @@ -882,20 +882,20 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| else { | |
| socksreq[len++] = 3; | |
| socksreq[len++] = (char) hostname_len; /* one byte address length */ | |
| - | memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ |
| + | memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */ |
| len += hostname_len; | |
| } | |
| infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", | |
| - | hostname, remote_port); |
| + | sx->hostname, sx->remote_port); |
| } | |
| /* FALLTHROUGH */ | |
| CONNECT_REQ_SEND: | |
| case CONNECT_REQ_SEND: | |
| /* PORT MSB */ | |
| - | socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); |
| + | socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff); |
| /* PORT LSB */ | |
| - | socksreq[len++] = (unsigned char)(remote_port & 0xff); |
| + | socksreq[len++] = (unsigned char)(sx->remote_port & 0xff); |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| if(conn->socks5_gssapi_enctype) { | |
| @@ -905,7 +905,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| #endif | |
| sx->outp = socksreq; | |
| sx->outstanding = len; | |
| - | sxstate(data, CONNECT_REQ_SENDING); |
| + | sxstate(sx, data, CONNECT_REQ_SENDING); |
| /* FALLTHROUGH */ | |
| case CONNECT_REQ_SENDING: | |
| result = Curl_write_plain(data, sockfd, (char *)sx->outp, | |
| @@ -928,7 +928,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| #endif | |
| sx->outstanding = 10; /* minimum packet size is 10 */ | |
| sx->outp = socksreq; | |
| - | sxstate(data, CONNECT_REQ_READ); |
| + | sxstate(sx, data, CONNECT_REQ_READ); |
| /* FALLTHROUGH */ | |
| case CONNECT_REQ_READ: | |
| result = Curl_read_plain(sockfd, (char *)sx->outp, | |
| @@ -958,7 +958,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; | |
| int code = socksreq[1]; | |
| failf(data, "Can't complete SOCKS5 connection to %s. (%d)", | |
| - | hostname, (unsigned char)socksreq[1]); |
| + | sx->hostname, (unsigned char)socksreq[1]); |
| if(code < 9) { | |
| /* RFC 1928 section 6 lists: */ | |
| static const CURLproxycode lookup[] = { | |
| @@ -1019,10 +1019,10 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| if(len > 10) { | |
| sx->outstanding = len - 10; /* get the rest */ | |
| sx->outp = &socksreq[10]; | |
| - | sxstate(data, CONNECT_REQ_READ_MORE); |
| + | sxstate(sx, data, CONNECT_REQ_READ_MORE); |
| } | |
| else { | |
| - | sxstate(data, CONNECT_DONE); |
| + | sxstate(sx, data, CONNECT_DONE); |
| break; | |
| } | |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| @@ -1047,12 +1047,197 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, | |
| sx->outp += actualread; | |
| return CURLPX_OK; | |
| } | |
| - | sxstate(data, CONNECT_DONE); |
| + | sxstate(sx, data, CONNECT_DONE); |
| } | |
| infof(data, "SOCKS5 request granted."); | |
| - | *done = TRUE; |
| return CURLPX_OK; /* Proxy was successful! */ | |
| } | |
| + | static CURLcode connect_SOCKS(struct Curl_cfilter *cf, |
| + | struct socks_state *sxstate, |
| + | struct Curl_easy *data) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | CURLproxycode pxresult = CURLPX_OK; |
| + | struct connectdata *conn = cf->conn; |
| + | |
| + | switch(conn->socks_proxy.proxytype) { |
| + | case CURLPROXY_SOCKS5: |
| + | case CURLPROXY_SOCKS5_HOSTNAME: |
| + | pxresult = do_SOCKS5(cf, sxstate, data); |
| + | break; |
| + | |
| + | case CURLPROXY_SOCKS4: |
| + | case CURLPROXY_SOCKS4A: |
| + | pxresult = do_SOCKS4(cf, sxstate, data); |
| + | break; |
| + | |
| + | default: |
| + | failf(data, "unknown proxytype option given"); |
| + | result = CURLE_COULDNT_CONNECT; |
| + | } /* switch proxytype */ |
| + | if(pxresult) { |
| + | result = CURLE_PROXY; |
| + | data->info.pxcode = pxresult; |
| + | } |
| + | |
| + | return result; |
| + | } |
| + | |
| + | static void socks_proxy_cf_free(struct Curl_cfilter *cf) |
| + | { |
| + | struct socks_state *sxstate = cf->ctx; |
| + | if(sxstate) { |
| + | free(sxstate); |
| + | cf->ctx = NULL; |
| + | } |
| + | } |
| + | |
| + | /* After a TCP connection to the proxy has been verified, this function does |
| + | the next magic steps. If 'done' isn't set TRUE, it is not done yet and |
| + | must be called again. |
| + | |
| + | Note: this function's sub-functions call failf() |
| + | |
| + | */ |
| + | static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool blocking, bool *done) |
| + | { |
| + | CURLcode result; |
| + | struct connectdata *conn = cf->conn; |
| + | int sockindex = cf->sockindex; |
| + | struct socks_state *sx = cf->ctx; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | result = cf->next->cft->connect(cf->next, data, blocking, done); |
| + | if(result || !*done) |
| + | return result; |
| + | |
| + | if(!sx) { |
| + | sx = calloc(sizeof(*sx), 1); |
| + | if(!sx) |
| + | return CURLE_OUT_OF_MEMORY; |
| + | cf->ctx = sx; |
| + | } |
| + | |
| + | if(sx->state == CONNECT_INIT) { |
| + | /* for the secondary socket (FTP), use the "connect to host" |
| + | * but ignore the "connect to port" (use the secondary port) |
| + | */ |
| + | sxstate(sx, data, CONNECT_SOCKS_INIT); |
| + | sx->hostname = |
| + | conn->bits.httpproxy ? |
| + | conn->http_proxy.host.name : |
| + | conn->bits.conn_to_host ? |
| + | conn->conn_to_host.name : |
| + | sockindex == SECONDARYSOCKET ? |
| + | conn->secondaryhostname : conn->host.name; |
| + | sx->remote_port = |
| + | conn->bits.httpproxy ? (int)conn->http_proxy.port : |
| + | sockindex == SECONDARYSOCKET ? conn->secondary_port : |
| + | conn->bits.conn_to_port ? conn->conn_to_port : |
| + | conn->remote_port; |
| + | sx->proxy_user = conn->socks_proxy.user; |
| + | sx->proxy_password = conn->socks_proxy.passwd; |
| + | } |
| + | |
| + | 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); |
| + | } |
| + | |
| + | *done = cf->connected; |
| + | return result; |
| + | } |
| + | |
| + | static int socks_cf_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks) |
| + | { |
| + | struct socks_state *sx = cf->ctx; |
| + | int fds; |
| + | |
| + | fds = cf->next->cft->get_select_socks(cf->next, data, socks); |
| + | 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]; |
| + | switch(sx->state) { |
| + | case CONNECT_RESOLVING: |
| + | case CONNECT_SOCKS_READ: |
| + | case CONNECT_AUTH_READ: |
| + | case CONNECT_REQ_READ: |
| + | case CONNECT_REQ_READ_MORE: |
| + | fds = GETSOCK_READSOCK(0); |
| + | break; |
| + | default: |
| + | fds = GETSOCK_WRITESOCK(0); |
| + | break; |
| + | } |
| + | } |
| + | return fds; |
| + | } |
| + | |
| + | static void socks_proxy_cf_close(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | |
| + | DEBUGASSERT(cf->next); |
| + | cf->connected = FALSE; |
| + | socks_proxy_cf_free(cf); |
| + | cf->next->cft->close(cf->next, data); |
| + | } |
| + | |
| + | static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | (void)data; |
| + | 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 const struct Curl_cftype cft_socks_proxy = { |
| + | "SOCKS-PROXYY", |
| + | socks_proxy_cf_destroy, |
| + | Curl_cf_def_attach_data, |
| + | socks_proxy_cf_detach_data, |
| + | Curl_cf_def_setup, |
| + | socks_proxy_cf_close, |
| + | socks_proxy_cf_connect, |
| + | socks_cf_get_select_socks, |
| + | Curl_cf_def_data_pending, |
| + | Curl_cf_def_send, |
| + | Curl_cf_def_recv, |
| + | }; |
| + | |
| + | CURLcode Curl_cfilter_socks_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_socks_proxy, NULL); |
| + | if(!result) |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | return result; |
| + | } |
| + | |
| #endif /* CURL_DISABLE_PROXY */ | |
| @@ -43,32 +43,6 @@ int Curl_blockread_all(struct Curl_easy *data, | |
| ssize_t buffersize, | |
| ssize_t *n); | |
| - | int Curl_SOCKS_getsock(struct connectdata *conn, |
| - | curl_socket_t *sock, |
| - | int sockindex); |
| - | /* |
| - | * This function logs in to a SOCKS4(a) proxy and sends the specifics to the |
| - | * final destination server. |
| - | */ |
| - | CURLproxycode Curl_SOCKS4(const char *proxy_name, |
| - | const char *hostname, |
| - | int remote_port, |
| - | int sockindex, |
| - | struct Curl_easy *data, |
| - | bool *done); |
| - | |
| - | /* |
| - | * This function logs in to a SOCKS5 proxy and sends the specifics to the |
| - | * final destination server. |
| - | */ |
| - | CURLproxycode Curl_SOCKS5(const char *proxy_name, |
| - | const char *proxy_password, |
| - | const char *hostname, |
| - | int remote_port, |
| - | int sockindex, |
| - | struct Curl_easy *data, |
| - | bool *done); |
| - | |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| /* | |
| * This function handles the SOCKS5 GSS-API negotiation and initialization | |
| @@ -77,6 +51,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, | |
| struct Curl_easy *data); | |
| #endif | |
| + | CURLcode Curl_cfilter_socks_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | |
| #endif /* CURL_DISABLE_PROXY */ | |
| #endif /* HEADER_CURL_SOCKS_H */ | |
| @@ -1491,6 +1491,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) | |
| } | |
| while(keepon) { | |
| + | DEBUGF(infof(data, "telnet_do(handle=%p), poll %d fds", data, poll_cnt)); |
| switch(Curl_poll(pfd, poll_cnt, interval_ms)) { | |
| case -1: /* error, stop reading */ | |
| keepon = FALSE; | |
| @@ -1509,6 +1510,14 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) | |
| /* returned not-zero, this an error */ | |
| if(result) { | |
| keepon = FALSE; | |
| + | /* TODO: in test 1452, macOS sees a ECONNRESET sometimes? |
| + | * Is this the telnet test server not shutting down the socket |
| + | * in a clean way? Seems to be timing related, happens more |
| + | * on slow debug build */ |
| + | if(data->state.os_errno == ECONNRESET) { |
| + | DEBUGF(infof(data, "telnet_do(handle=%p), unexpected ECONNRESET" |
| + | " on recv", data)); |
| + | } |
| break; | |
| } | |
| /* returned zero but actually received 0 or less here, | |
| @@ -64,6 +64,7 @@ | |
| #include "content_encoding.h" | |
| #include "hostip.h" | |
| + | #include "cfilters.h" |
| #include "transfer.h" | |
| #include "sendf.h" | |
| #include "speedcheck.h" | |
| @@ -454,7 +455,7 @@ static int data_pending(const struct Curl_easy *data) | |
| #endif | |
| if(conn->handler->protocol&PROTO_FAMILY_FTP) | |
| - | return Curl_ssl_data_pending(conn, SECONDARYSOCKET); |
| + | return Curl_cfilter_data_pending(data, conn, 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 */ | |
| @@ -469,7 +470,7 @@ static int data_pending(const struct Curl_easy *data) | |
| a workaround, we return nonzero here to call http2_recv. */ | |
| ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) || | |
| #endif | |
| - | Curl_ssl_data_pending(conn, FIRSTSOCKET); |
| + | Curl_cfilter_data_pending(data, conn, FIRSTSOCKET); |
| } | |
| /* | |
| @@ -569,11 +570,13 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread); | |
| /* read would've blocked */ | |
| - | if(CURLE_AGAIN == result) |
| + | if(CURLE_AGAIN == result) { |
| + | result = CURLE_OK; |
| break; /* get out of loop */ | |
| + | } |
| if(result>0) | |
| - | return result; |
| + | goto out; |
| } | |
| else { | |
| /* read nothing but since we wanted nothing we consider this an OK | |
| @@ -619,7 +622,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| if(conn->handler->readwrite) { | |
| result = conn->handler->readwrite(data, conn, &nread, &readmore); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(readmore) | |
| break; | |
| } | |
| @@ -632,13 +635,13 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| bool stop_reading = FALSE; | |
| result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(conn->handler->readwrite && | |
| (k->maxdownload <= 0 && nread > 0)) { | |
| result = conn->handler->readwrite(data, conn, &nread, &readmore); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(readmore) | |
| break; | |
| } | |
| @@ -669,7 +672,8 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| /* data arrives although we want none, bail out */ | |
| streamclose(conn, "ignoring body"); | |
| *done = TRUE; | |
| - | return CURLE_WEIRD_SERVER_REPLY; |
| + | result = CURLE_WEIRD_SERVER_REPLY; |
| + | goto out; |
| } | |
| #ifndef CURL_DISABLE_HTTP | |
| @@ -680,7 +684,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| /* HTTP-only checks */ | |
| result = Curl_http_firstwrite(data, conn, done); | |
| if(result || *done) | |
| - | return result; |
| + | goto out; |
| } | |
| } /* this is the first time we write a body part */ | |
| #endif /* CURL_DISABLE_HTTP */ | |
| @@ -717,10 +721,12 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| if(CHUNKE_OK < res) { | |
| if(CHUNKE_PASSTHRU_ERROR == res) { | |
| failf(data, "Failed reading the chunked-encoded stream"); | |
| - | return extra; |
| + | result = extra; |
| + | goto out; |
| } | |
| failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res)); | |
| - | return CURLE_RECV_ERROR; |
| + | result = CURLE_RECV_ERROR; |
| + | goto out; |
| } | |
| if(CHUNKE_STOP == res) { | |
| /* we're done reading chunks! */ | |
| @@ -796,7 +802,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| (size_t)k->maxdownload); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| } | |
| if(k->badheader < HEADER_ALLBAD) { | |
| /* This switch handles various content encodings. If there's an | |
| @@ -821,7 +827,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| k->badheader = HEADER_NORMAL; /* taken care of now */ | |
| if(result) | |
| - | return result; |
| + | goto out; |
| } | |
| } /* if(!header and data to read) */ | |
| @@ -839,7 +845,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| result = conn->handler->readwrite(data, conn, &nread, &readmore); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(readmore) | |
| k->keepon |= KEEP_RECV; /* we're not done reading */ | |
| @@ -874,7 +880,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, | |
| k->keepon &= ~KEEP_SEND; /* no writing anymore either */ | |
| } | |
| - | return CURLE_OK; |
| + | out: |
| + | DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result)); |
| + | return result; |
| } | |
| CURLcode Curl_done_sending(struct Curl_easy *data, | |
| @@ -1201,14 +1209,15 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| if(select_res == CURL_CSELECT_ERR) { | |
| failf(data, "select/poll returned error"); | |
| - | return CURLE_SEND_ERROR; |
| + | result = CURLE_SEND_ERROR; |
| + | goto out; |
| } | |
| #ifdef USE_HYPER | |
| if(conn->datastream) { | |
| result = conn->datastream(data, conn, &didwhat, done, select_res); | |
| if(result || *done) | |
| - | return result; |
| + | goto out; |
| } | |
| else { | |
| #endif | |
| @@ -1218,7 +1227,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) { | |
| result = readwrite_data(data, conn, k, &didwhat, done, comeback); | |
| if(result || *done) | |
| - | return result; |
| + | goto out; |
| } | |
| /* If we still have writing to do, we check if we have a writable socket. */ | |
| @@ -1227,7 +1236,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| result = readwrite_upload(data, conn, &didwhat); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| } | |
| #ifdef USE_HYPER | |
| } | |
| @@ -1264,7 +1273,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| if(conn->transport == TRNSPRT_QUIC) { | |
| result = Curl_quic_idle(data); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| } | |
| #endif | |
| } | |
| @@ -1274,7 +1283,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| else | |
| result = Curl_speedcheck(data, k->now); | |
| if(result) | |
| - | return result; |
| + | goto out; |
| if(k->keepon) { | |
| if(0 > Curl_timeleft(data, &k->now, FALSE)) { | |
| @@ -1291,7 +1300,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| Curl_timediff(k->now, data->progress.t_startsingle), | |
| k->bytecount); | |
| } | |
| - | return CURLE_OPERATION_TIMEDOUT; |
| + | result = CURLE_OPERATION_TIMEDOUT; |
| + | goto out; |
| } | |
| } | |
| else { | |
| @@ -1312,7 +1322,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| !k->newurl) { | |
| failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T | |
| " bytes remaining to read", k->size - k->bytecount); | |
| - | return CURLE_PARTIAL_FILE; |
| + | result = CURLE_PARTIAL_FILE; |
| + | goto out; |
| } | |
| if(!(data->set.opt_no_body) && k->chunk && | |
| (conn->chunk.state != CHUNK_STOP)) { | |
| @@ -1326,17 +1337,22 @@ CURLcode Curl_readwrite(struct connectdata *conn, | |
| * | |
| */ | |
| failf(data, "transfer closed with outstanding read data remaining"); | |
| - | return CURLE_PARTIAL_FILE; |
| + | result = CURLE_PARTIAL_FILE; |
| + | goto out; |
| + | } |
| + | if(Curl_pgrsUpdate(data)) { |
| + | result = CURLE_ABORTED_BY_CALLBACK; |
| + | goto out; |
| } | |
| - | if(Curl_pgrsUpdate(data)) |
| - | return CURLE_ABORTED_BY_CALLBACK; |
| } | |
| /* Now update the "done" boolean we return */ | |
| *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND| | |
| KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE; | |
| - | |
| - | return CURLE_OK; |
| + | result = CURLE_OK; |
| + | out: |
| + | DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result)); |
| + | return result; |
| } | |
| /* | |
| @@ -107,6 +107,7 @@ bool Curl_win32_idn_to_ascii(const char *in, char **out); | |
| #include "system_win32.h" | |
| #include "hsts.h" | |
| #include "noproxy.h" | |
| + | #include "cfilters.h" |
| /* And now for the protocols */ | |
| #include "ftp.h" | |
| @@ -140,7 +141,11 @@ bool Curl_win32_idn_to_ascii(const char *in, char **out); | |
| #include "curl_memory.h" | |
| #include "memdebug.h" | |
| - | static void conn_free(struct connectdata *conn); |
| + | #ifndef ARRAYSIZE |
| + | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) |
| + | #endif |
| + | |
| + | static void conn_free(struct Curl_easy *data, struct connectdata *conn); |
| /* Some parts of the code (e.g. chunked encoding) assume this buffer has at | |
| * more than just a few bytes to play with. Don't let it become too small or | |
| @@ -756,28 +761,20 @@ static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn) | |
| /* possible left-overs from the async name resolvers */ | |
| Curl_resolver_cancel(data); | |
| - | /* close the SSL stuff before we close any sockets since they will/may |
| - | write to the sockets */ |
| - | Curl_ssl_close(data, conn, FIRSTSOCKET); |
| - | #ifndef CURL_DISABLE_FTP |
| - | Curl_ssl_close(data, conn, SECONDARYSOCKET); |
| - | #endif |
| - | |
| - | /* close possibly still open sockets */ |
| - | if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) |
| - | Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); |
| - | if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) |
| - | Curl_closesocket(data, conn, conn->sock[FIRSTSOCKET]); |
| - | if(CURL_SOCKET_BAD != conn->tempsock[0]) |
| - | Curl_closesocket(data, conn, conn->tempsock[0]); |
| - | if(CURL_SOCKET_BAD != conn->tempsock[1]) |
| - | Curl_closesocket(data, conn, conn->tempsock[1]); |
| + | Curl_cfilter_close(data, conn, SECONDARYSOCKET); |
| + | Curl_cfilter_close(data, conn, FIRSTSOCKET); |
| } | |
| - | static void conn_free(struct connectdata *conn) |
| + | static void conn_free(struct Curl_easy *data, struct connectdata *conn) |
| { | |
| + | size_t i; |
| + | |
| DEBUGASSERT(conn); | |
| + | for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { |
| + | Curl_cfilter_destroy(data, conn, (int)i); |
| + | } |
| + | |
| Curl_free_idnconverted_hostname(&conn->host); | |
| Curl_free_idnconverted_hostname(&conn->conn_to_host); | |
| #ifndef CURL_DISABLE_PROXY | |
| @@ -801,7 +798,6 @@ static void conn_free(struct connectdata *conn) | |
| Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ | |
| Curl_safefree(conn->hostname_resolve); | |
| Curl_safefree(conn->secondaryhostname); | |
| - | Curl_safefree(conn->connect_state); |
| conn_reset_all_postponed_data(conn); | |
| Curl_llist_destroy(&conn->easyq, NULL); | |
| @@ -847,6 +843,8 @@ void Curl_disconnect(struct Curl_easy *data, | |
| /* the transfer must be detached from the connection */ | |
| DEBUGASSERT(!data->conn); | |
| + | DEBUGF(infof(data, "Curl_disconnect(conn #%ld, dead=%d)", |
| + | conn->connection_id, dead_connection)); |
| /* | |
| * If this connection isn't marked to force-close, leave it open if there | |
| * are other users of it | |
| @@ -884,7 +882,7 @@ void Curl_disconnect(struct Curl_easy *data, | |
| /* detach it again */ | |
| Curl_detach_connection(data); | |
| - | conn_free(conn); |
| + | conn_free(data, conn); |
| } | |
| /* | |
| @@ -1687,7 +1685,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) | |
| Note that these backend pointers can be swapped by vtls (eg ssl backend | |
| data becomes proxy backend data). */ | |
| { | |
| - | size_t onesize = Curl_ssl->sizeof_ssl_backend_data; |
| + | size_t onesize = Curl_ssl_get_backend_data_size(data); |
| size_t totalsize = onesize; | |
| char *ssl; | |
| @@ -2435,7 +2433,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, | |
| } | |
| #ifdef USE_SSL | |
| - | if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) |
| + | if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) |
| #endif | |
| if(proxytype == CURLPROXY_HTTPS) { | |
| failf(data, "Unsupported proxy \'%s\', libcurl is built without the " | |
| @@ -3529,13 +3527,13 @@ static CURLcode resolve_server(struct Curl_easy *data, | |
| } | |
| /* | |
| - | * Cleanup the connection just allocated before we can move along and use the |
| - | * previously existing one. All relevant data is copied over and old_conn is |
| - | * ready for freeing once this function returns. |
| + | * Cleanup the connection `temp`, just allocated for `data`, before using the |
| + | * previously `existing` one for `data`. All relevant info is copied over |
| + | * and `temp` is freed. |
| */ | |
| static void reuse_conn(struct Curl_easy *data, | |
| - | struct connectdata *old_conn, |
| - | struct connectdata *conn) |
| + | 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 | |
| @@ -3543,66 +3541,66 @@ static void reuse_conn(struct Curl_easy *data, | |
| char local_ip[MAX_IPADR_LEN] = ""; | |
| int local_port = -1; | |
| - | /* get the user+password information from the old_conn struct since it may |
| + | /* 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(old_conn->user) { |
| + | if(temp->user) { |
| /* use the new user name and password though */ | |
| - | Curl_safefree(conn->user); |
| - | Curl_safefree(conn->passwd); |
| - | conn->user = old_conn->user; |
| - | conn->passwd = old_conn->passwd; |
| - | old_conn->user = NULL; |
| - | old_conn->passwd = NULL; |
| + | Curl_safefree(existing->user); |
| + | Curl_safefree(existing->passwd); |
| + | existing->user = temp->user; |
| + | existing->passwd = temp->passwd; |
| + | temp->user = NULL; |
| + | temp->passwd = NULL; |
| } | |
| #ifndef CURL_DISABLE_PROXY | |
| - | conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; |
| - | if(conn->bits.proxy_user_passwd) { |
| + | existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; |
| + | if(existing->bits.proxy_user_passwd) { |
| /* use the new proxy user name and proxy password though */ | |
| - | Curl_safefree(conn->http_proxy.user); |
| - | Curl_safefree(conn->socks_proxy.user); |
| - | Curl_safefree(conn->http_proxy.passwd); |
| - | Curl_safefree(conn->socks_proxy.passwd); |
| - | conn->http_proxy.user = old_conn->http_proxy.user; |
| - | conn->socks_proxy.user = old_conn->socks_proxy.user; |
| - | conn->http_proxy.passwd = old_conn->http_proxy.passwd; |
| - | conn->socks_proxy.passwd = old_conn->socks_proxy.passwd; |
| - | old_conn->http_proxy.user = NULL; |
| - | old_conn->socks_proxy.user = NULL; |
| - | old_conn->http_proxy.passwd = NULL; |
| - | old_conn->socks_proxy.passwd = NULL; |
| - | } |
| - | #endif |
| - | |
| - | Curl_free_idnconverted_hostname(&conn->host); |
| - | Curl_free_idnconverted_hostname(&conn->conn_to_host); |
| - | Curl_safefree(conn->host.rawalloc); |
| - | Curl_safefree(conn->conn_to_host.rawalloc); |
| - | conn->host = old_conn->host; |
| - | old_conn->host.rawalloc = NULL; |
| - | old_conn->host.encalloc = NULL; |
| - | conn->conn_to_host = old_conn->conn_to_host; |
| - | old_conn->conn_to_host.rawalloc = NULL; |
| - | conn->conn_to_port = old_conn->conn_to_port; |
| - | conn->remote_port = old_conn->remote_port; |
| - | Curl_safefree(conn->hostname_resolve); |
| - | |
| - | conn->hostname_resolve = old_conn->hostname_resolve; |
| - | old_conn->hostname_resolve = NULL; |
| + | Curl_safefree(existing->http_proxy.user); |
| + | Curl_safefree(existing->socks_proxy.user); |
| + | Curl_safefree(existing->http_proxy.passwd); |
| + | Curl_safefree(existing->socks_proxy.passwd); |
| + | existing->http_proxy.user = temp->http_proxy.user; |
| + | existing->socks_proxy.user = temp->socks_proxy.user; |
| + | existing->http_proxy.passwd = temp->http_proxy.passwd; |
| + | existing->socks_proxy.passwd = temp->socks_proxy.passwd; |
| + | temp->http_proxy.user = NULL; |
| + | temp->socks_proxy.user = NULL; |
| + | temp->http_proxy.passwd = NULL; |
| + | temp->socks_proxy.passwd = NULL; |
| + | } |
| + | #endif |
| + | |
| + | Curl_free_idnconverted_hostname(&existing->host); |
| + | Curl_free_idnconverted_hostname(&existing->conn_to_host); |
| + | Curl_safefree(existing->host.rawalloc); |
| + | Curl_safefree(existing->conn_to_host.rawalloc); |
| + | existing->host = temp->host; |
| + | temp->host.rawalloc = NULL; |
| + | temp->host.encalloc = NULL; |
| + | existing->conn_to_host = temp->conn_to_host; |
| + | temp->conn_to_host.rawalloc = NULL; |
| + | existing->conn_to_port = temp->conn_to_port; |
| + | existing->remote_port = temp->remote_port; |
| + | Curl_safefree(existing->hostname_resolve); |
| + | |
| + | existing->hostname_resolve = temp->hostname_resolve; |
| + | temp->hostname_resolve = NULL; |
| /* persist connection info in session handle */ | |
| - | if(conn->transport == TRNSPRT_TCP) { |
| - | Curl_conninfo_local(data, conn->sock[FIRSTSOCKET], |
| + | if(existing->transport == TRNSPRT_TCP) { |
| + | Curl_conninfo_local(data, existing->sock[FIRSTSOCKET], |
| local_ip, &local_port); | |
| } | |
| - | Curl_persistconninfo(data, conn, local_ip, local_port); |
| + | Curl_persistconninfo(data, existing, local_ip, local_port); |
| - | conn_reset_all_postponed_data(old_conn); /* free buffers */ |
| + | conn_reset_all_postponed_data(temp); /* free buffers */ |
| /* re-use init */ | |
| - | conn->bits.reuse = TRUE; /* yes, we're re-using here */ |
| + | existing->bits.reuse = TRUE; /* yes, we're re-using here */ |
| - | conn_free(old_conn); |
| + | conn_free(data, temp); |
| } | |
| /** | |
| @@ -3626,7 +3624,7 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| { | |
| CURLcode result = CURLE_OK; | |
| struct connectdata *conn; | |
| - | struct connectdata *conn_temp = NULL; |
| + | struct connectdata *existing = NULL; |
| bool reuse; | |
| bool connections_available = TRUE; | |
| bool force_reuse = FALSE; | |
| @@ -3768,13 +3766,6 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| if(result) | |
| goto out; | |
| - | conn->recv[FIRSTSOCKET] = Curl_recv_plain; |
| - | conn->send[FIRSTSOCKET] = Curl_send_plain; |
| - | conn->recv[SECONDARYSOCKET] = Curl_recv_plain; |
| - | conn->send[SECONDARYSOCKET] = Curl_send_plain; |
| - | |
| - | conn->bits.tcp_fastopen = data->set.tcp_fastopen; |
| - | |
| /*********************************************************************** | |
| * file: is a special case in that it doesn't need a network connection | |
| ***********************************************************************/ | |
| @@ -3789,8 +3780,6 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| /* Setup a "faked" transfer that'll do nothing */ | |
| if(!result) { | |
| - | conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ |
| - | |
| Curl_attach_connection(data, conn); | |
| result = Curl_conncache_add_conn(data); | |
| if(result) | |
| @@ -3816,6 +3805,13 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| } | |
| #endif | |
| + | /* Setup filter for network connections */ |
| + | conn->recv[FIRSTSOCKET] = Curl_cfilter_recv; |
| + | conn->send[FIRSTSOCKET] = Curl_cfilter_send; |
| + | conn->recv[SECONDARYSOCKET] = Curl_cfilter_recv; |
| + | conn->send[SECONDARYSOCKET] = Curl_cfilter_send; |
| + | conn->bits.tcp_fastopen = data->set.tcp_fastopen; |
| + | |
| /* Get a cloned copy of the SSL config situation stored in the | |
| connection struct. But to get this going nicely, we must first make | |
| sure that the strings in the master copy are pointing to the correct | |
| @@ -3915,16 +3911,16 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| data->set.connect_only) | |
| reuse = FALSE; | |
| else | |
| - | reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); |
| + | reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe); |
| if(reuse) { | |
| /* | |
| * We already have a connection for this, we got the former connection in | |
| - | * the conn_temp variable and thus we need to cleanup the one we just |
| - | * allocated before we can move along and use the previously existing one. |
| + | * `existing` and thus we need to cleanup the one we just |
| + | * allocated before we can move along and use `existing`. |
| */ | |
| - | reuse_conn(data, conn, conn_temp); |
| - | conn = conn_temp; |
| + | reuse_conn(data, conn, existing); |
| + | conn = existing; |
| *in_connect = conn; | |
| #ifndef CURL_DISABLE_PROXY | |
| @@ -3999,7 +3995,7 @@ static CURLcode create_conn(struct Curl_easy *data, | |
| if(!connections_available) { | |
| infof(data, "No connections available."); | |
| - | conn_free(conn); |
| + | conn_free(data, conn); |
| *in_connect = NULL; | |
| result = CURLE_NO_CONNECTION_AVAILABLE; | |
| @@ -4082,7 +4078,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, | |
| *protocol_done = TRUE; | |
| return result; | |
| } | |
| - | *protocol_done = FALSE; /* default to not done */ |
| #ifndef CURL_DISABLE_PROXY | |
| /* set proxy_connect_closed to false unconditionally already here since it | |
| @@ -4099,26 +4094,11 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, | |
| /* set start time here for timeout purposes in the connect procedure, it | |
| is later set again for the progress meter purpose */ | |
| conn->now = Curl_now(); | |
| - | |
| - | if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { |
| - | conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; |
| - | result = Curl_connecthost(data, conn, conn->dns_entry); |
| - | if(result) |
| - | return result; |
| - | } |
| - | else { |
| - | Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ |
| - | if(conn->ssl[FIRSTSOCKET].use || |
| - | (conn->handler->protocol & PROTO_FAMILY_SSH)) |
| - | Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ |
| - | conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; |
| - | *protocol_done = TRUE; |
| - | Curl_updateconninfo(data, conn, conn->sock[FIRSTSOCKET]); |
| - | Curl_verboseconnect(data, conn); |
| - | } |
| - | |
| - | conn->now = Curl_now(); /* time this *after* the connect is done, we set |
| - | this here perhaps a second time */ |
| + | if(!conn->bits.reuse) |
| + | result = Curl_cfilter_setup(data, conn, FIRSTSOCKET, conn->dns_entry, |
| + | CURL_CF_SSL_DEFAULT); |
| + | /* not sure we need this flag to be passed around any more */ |
| + | *protocol_done = FALSE; |
| return result; | |
| } | |
| @@ -64,21 +64,4 @@ void Curl_free_idnconverted_hostname(struct hostname *host); | |
| void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn); | |
| #endif | |
| - | #ifdef CURL_DISABLE_PROXY |
| - | #define CONNECT_PROXY_SSL() FALSE |
| - | #else |
| - | |
| - | #define CONNECT_PROXY_SSL()\ |
| - | (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ |
| - | !conn->bits.proxy_ssl_connected[sockindex]) |
| - | |
| - | #define CONNECT_FIRSTSOCKET_PROXY_SSL()\ |
| - | (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ |
| - | !conn->bits.proxy_ssl_connected[FIRSTSOCKET]) |
| - | |
| - | #define CONNECT_SECONDARYSOCKET_PROXY_SSL()\ |
| - | (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ |
| - | !conn->bits.proxy_ssl_connected[SECONDARYSOCKET]) |
| - | #endif /* !CURL_DISABLE_PROXY */ |
| - | |
| #endif /* HEADER_CURL_URL_H */ | |
| @@ -477,8 +477,6 @@ struct negotiatedata { | |
| * Boolean values that concerns this connection. | |
| */ | |
| struct ConnectBits { | |
| - | bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set |
| - | the first time on the first connect function call */ |
| #ifndef CURL_DISABLE_PROXY | |
| bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy | |
| is complete */ | |
| @@ -874,38 +872,6 @@ struct proxy_info { | |
| }; | |
| struct ldapconninfo; | |
| - | struct http_connect_state; |
| - | |
| - | /* for the (SOCKS) connect state machine */ |
| - | enum connect_t { |
| - | CONNECT_INIT, |
| - | CONNECT_SOCKS_INIT, /* 1 */ |
| - | CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ |
| - | CONNECT_SOCKS_READ_INIT, /* 3 set up read */ |
| - | CONNECT_SOCKS_READ, /* 4 read server response */ |
| - | CONNECT_GSSAPI_INIT, /* 5 */ |
| - | CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ |
| - | CONNECT_AUTH_SEND, /* 7 send auth */ |
| - | CONNECT_AUTH_READ, /* 8 read auth response */ |
| - | CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ |
| - | CONNECT_RESOLVING, /* 10 */ |
| - | CONNECT_RESOLVED, /* 11 */ |
| - | CONNECT_RESOLVE_REMOTE, /* 12 */ |
| - | CONNECT_REQ_SEND, /* 13 */ |
| - | CONNECT_REQ_SENDING, /* 14 */ |
| - | CONNECT_REQ_READ, /* 15 */ |
| - | CONNECT_REQ_READ_MORE, /* 16 */ |
| - | CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ |
| - | }; |
| - | |
| - | #define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) && \ |
| - | ((x) < CONNECT_DONE)) |
| - | |
| - | struct connstate { |
| - | enum connect_t state; |
| - | ssize_t outstanding; /* send this many bytes more */ |
| - | unsigned char *outp; /* send from this pointer */ |
| - | }; |
| #define TRNSPRT_TCP 3 | |
| #define TRNSPRT_UDP 4 | |
| @@ -917,7 +883,6 @@ struct connstate { | |
| * unique for an entire connection. | |
| */ | |
| struct connectdata { | |
| - | struct connstate cnnct; |
| struct Curl_llist_element bundle_node; /* conncache */ | |
| /* chunk is for HTTP chunked encoding, but is in the general connectdata | |
| @@ -986,6 +951,7 @@ struct connectdata { | |
| 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 */ | |
| @@ -1113,7 +1079,6 @@ struct connectdata { | |
| #endif | |
| } proto; | |
| - | struct http_connect_state *connect_state; /* for HTTP CONNECT */ |
| struct connectbundle *bundle; /* The bundle we are member of */ | |
| #ifdef USE_UNIX_SOCKETS | |
| char *unix_domain_socket; | |
| @@ -522,7 +522,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp) | |
| Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); | |
| version_info.ssl_version = ssl_buffer; | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY) |
| + | if(Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY)) |
| version_info.features |= CURL_VERSION_HTTPS_PROXY; | |
| else | |
| version_info.features &= ~CURL_VERSION_HTTPS_PROXY; | |
| @@ -71,6 +71,7 @@ | |
| #include "strdup.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "inet_ntop.h" | |
| #include "parsedate.h" /* for the week day and month names */ | |
| @@ -2323,7 +2324,6 @@ CURLcode scp_perform(struct Curl_easy *data, | |
| bool *connected, bool *dophase_done) | |
| { | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -2334,7 +2334,7 @@ CURLcode scp_perform(struct Curl_easy *data, | |
| result = myssh_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) { | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -2504,7 +2504,6 @@ CURLcode sftp_perform(struct Curl_easy *data, | |
| bool *dophase_done) | |
| { | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -2516,7 +2515,7 @@ CURLcode sftp_perform(struct Curl_easy *data, | |
| /* run the state-machine */ | |
| result = myssh_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) { | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -74,6 +74,7 @@ | |
| #include "strdup.h" | |
| #include "strcase.h" | |
| #include "vtls/vtls.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "inet_ntop.h" | |
| #include "parsedate.h" /* for the week day and month names */ | |
| @@ -3374,7 +3375,6 @@ CURLcode scp_perform(struct Curl_easy *data, | |
| bool *dophase_done) | |
| { | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -3386,7 +3386,7 @@ CURLcode scp_perform(struct Curl_easy *data, | |
| /* run the state-machine */ | |
| result = ssh_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) { | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -3575,7 +3575,7 @@ CURLcode sftp_perform(struct Curl_easy *data, | |
| /* run the state-machine */ | |
| result = ssh_multi_statemach(data, dophase_done); | |
| - | *connected = data->conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) { | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -31,6 +31,7 @@ | |
| #include <wolfssh/ssh.h> | |
| #include <wolfssh/wolfsftp.h> | |
| #include "urldata.h" | |
| + | #include "cfilters.h" |
| #include "connect.h" | |
| #include "sendf.h" | |
| #include "progress.h" | |
| @@ -939,7 +940,6 @@ CURLcode wsftp_perform(struct Curl_easy *data, | |
| bool *dophase_done) | |
| { | |
| CURLcode result = CURLE_OK; | |
| - | struct connectdata *conn = data->conn; |
| DEBUGF(infof(data, "DO phase starts")); | |
| @@ -951,7 +951,7 @@ CURLcode wsftp_perform(struct Curl_easy *data, | |
| /* run the state-machine */ | |
| result = wssh_multi_statemach(data, dophase_done); | |
| - | *connected = conn->bits.tcpconnect[FIRSTSOCKET]; |
| + | *connected = Curl_cfilter_is_connected(data, data->conn, FIRSTSOCKET); |
| if(*dophase_done) { | |
| DEBUGF(infof(data, "DO phase is complete")); | |
| @@ -32,13 +32,17 @@ | |
| #include "sendf.h" | |
| #include "inet_pton.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "connect.h" | |
| #include "select.h" | |
| #include "multiif.h" | |
| #include "curl_printf.h" | |
| - | #include "curl_memory.h" |
| #include "strcase.h" | |
| + | /* The last #include files should be: */ |
| + | #include "curl_memory.h" |
| + | #include "memdebug.h" |
| + | |
| struct x509_context { | |
| const br_x509_class *vtable; | |
| br_x509_minimal_context minimal; | |
| @@ -1069,8 +1073,6 @@ static CURLcode bearssl_connect_common(struct Curl_easy *data, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = bearssl_recv; |
| - | conn->send[sockindex] = bearssl_send; |
| *done = TRUE; | |
| } | |
| else | |
| @@ -1156,12 +1158,15 @@ static void bearssl_close(struct Curl_easy *data, | |
| DEBUGASSERT(backend); | |
| if(backend->active) { | |
| + | backend->active = FALSE; |
| br_ssl_engine_close(&backend->ctx.eng); | |
| (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); | |
| } | |
| - | for(i = 0; i < backend->anchors_len; ++i) |
| - | free(backend->anchors[i].dn.data); |
| - | free(backend->anchors); |
| + | if(backend->anchors) { |
| + | for(i = 0; i < backend->anchors_len; ++i) |
| + | free(backend->anchors[i].dn.data); |
| + | Curl_safefree(backend->anchors); |
| + | } |
| } | |
| static void bearssl_session_free(void *ptr) | |
| @@ -1209,7 +1214,9 @@ const struct Curl_ssl Curl_ssl_bearssl = { | |
| bearssl_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | bearssl_recv, /* recv decrypted data */ |
| + | bearssl_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_BEARSSL */ | |
| @@ -73,6 +73,7 @@ | |
| #include "sendf.h" | |
| #include "gskit.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "connect.h" /* for the connect timeout */ | |
| #include "select.h" | |
| #include "strcase.h" | |
| @@ -1146,8 +1147,6 @@ static CURLcode gskit_connect_common(struct Curl_easy *data, | |
| else if(connssl->connecting_state == ssl_connect_done) { | |
| connssl->state = ssl_connection_complete; | |
| connssl->connecting_state = ssl_connect_1; | |
| - | conn->recv[sockindex] = gskit_recv; |
| - | conn->send[sockindex] = gskit_send; |
| *done = TRUE; | |
| } | |
| @@ -1324,7 +1323,9 @@ const struct Curl_ssl Curl_ssl_gskit = { | |
| NULL, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | gskit_recv, /* recv decrypted data */ |
| + | gskit_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_GSKIT */ | |
| @@ -45,6 +45,7 @@ | |
| #include "inet_pton.h" | |
| #include "gtls.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "vauth/vauth.h" | |
| #include "parsedate.h" | |
| #include "connect.h" /* for the connect timeout */ | |
| @@ -1383,8 +1384,6 @@ gtls_connect_common(struct Curl_easy *data, | |
| rc = Curl_gtls_verifyserver(data, conn, session, sockindex); | |
| if(rc) | |
| return rc; | |
| - | conn->recv[sockindex] = gtls_recv; |
| - | conn->send[sockindex] = gtls_send; |
| } | |
| *done = ssl_connect_1 == connssl->connecting_state; | |
| @@ -1700,7 +1699,9 @@ const struct Curl_ssl Curl_ssl_gnutls = { | |
| gtls_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | gtls_recv, /* recv decrypted data */ |
| + | gtls_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_GNUTLS */ | |
| @@ -61,6 +61,7 @@ | |
| #include "inet_pton.h" | |
| #include "mbedtls.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "parsedate.h" | |
| #include "connect.h" /* for the connect timeout */ | |
| #include "select.h" | |
| @@ -675,9 +676,6 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, | |
| DEBUGASSERT(backend); | |
| - | conn->recv[sockindex] = mbed_recv; |
| - | conn->send[sockindex] = mbed_send; |
| - | |
| ret = mbedtls_ssl_handshake(&backend->ssl); | |
| if(ret == MBEDTLS_ERR_SSL_WANT_READ) { | |
| @@ -1147,8 +1145,6 @@ mbed_connect_common(struct Curl_easy *data, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = mbed_recv; |
| - | conn->send[sockindex] = mbed_send; |
| *done = TRUE; | |
| } | |
| else | |
| @@ -1268,7 +1264,9 @@ const struct Curl_ssl Curl_ssl_mbedtls = { | |
| mbedtls_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | mbed_recv, /* recv decrypted data */ |
| + | mbed_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_MBEDTLS */ | |
| @@ -39,6 +39,7 @@ | |
| #include "strcase.h" | |
| #include "select.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "llist.h" | |
| #include "multiif.h" | |
| #include "curl_printf.h" | |
| @@ -2319,8 +2320,6 @@ static CURLcode nss_connect_common(struct Curl_easy *data, | |
| *done = TRUE; | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = nss_recv; |
| - | conn->send[sockindex] = nss_send; |
| /* ssl_connect_done is never used outside, go back to the initial state */ | |
| connssl->connecting_state = ssl_connect_1; | |
| @@ -2531,7 +2530,9 @@ const struct Curl_ssl Curl_ssl_nss = { | |
| nss_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | nss_recv, /* recv decrypted data */ |
| + | nss_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_NSS */ | |
| @@ -55,6 +55,7 @@ | |
| #include "slist.h" | |
| #include "select.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "vauth/vauth.h" | |
| #include "keylog.h" | |
| #include "strcase.h" | |
| @@ -4275,8 +4276,6 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = ossl_recv; |
| - | conn->send[sockindex] = ossl_send; |
| *done = TRUE; | |
| } | |
| else | |
| @@ -4776,7 +4775,9 @@ const struct Curl_ssl Curl_ssl_openssl = { | |
| #endif | |
| ossl_associate_connection, /* associate_connection */ | |
| ossl_disassociate_connection, /* disassociate_connection */ | |
| - | ossl_free_multi_ssl_backend_data /* free_multi_ssl_backend_data */ |
| + | ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ |
| + | ossl_recv, /* recv decrypted data */ |
| + | ossl_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_OPENSSL */ | |
| @@ -35,6 +35,7 @@ | |
| #include "urldata.h" | |
| #include "sendf.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "select.h" | |
| #include "strerror.h" | |
| #include "multiif.h" | |
| @@ -473,8 +474,6 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, | |
| cr_set_negotiated_alpn(data, conn, rconn); | |
| - | conn->recv[sockindex] = cr_recv; |
| - | conn->send[sockindex] = cr_send; |
| *done = TRUE; | |
| return CURLE_OK; | |
| } | |
| @@ -631,7 +630,9 @@ const struct Curl_ssl Curl_ssl_rustls = { | |
| NULL, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | cr_recv, /* recv decrypted data */ |
| + | cr_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_RUSTLS */ | |
| @@ -41,6 +41,7 @@ | |
| #include "schannel.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "strcase.h" | |
| #include "sendf.h" | |
| #include "connect.h" /* for the connect timeout */ | |
| @@ -1935,15 +1936,6 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | if(!connssl->backend->recv_renegotiating) { |
| - | /* On renegotiation, we don't want to reset the existing recv/send |
| - | * function pointers. They will have been set after the initial TLS |
| - | * handshake was completed. If they were subsequently modified, as |
| - | * is the case with HTTP/2, we don't want to override that change. |
| - | */ |
| - | conn->recv[sockindex] = schannel_recv; |
| - | conn->send[sockindex] = schannel_send; |
| - | } |
| #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS | |
| /* When SSPI is used in combination with Schannel | |
| @@ -2810,7 +2802,9 @@ const struct Curl_ssl Curl_ssl_schannel = { | |
| schannel_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | schannel_recv, /* recv decrypted data */ |
| + | schannel_send, /* send data to encrypt */ |
| }; | |
| #endif /* USE_SCHANNEL */ | |
| @@ -42,6 +42,7 @@ | |
| #ifdef HAS_MANUAL_VERIFY_API | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "sendf.h" | |
| #include "strerror.h" | |
| #include "curl_multibyte.h" | |
| @@ -127,6 +127,7 @@ | |
| #include "connect.h" | |
| #include "select.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "sectransp.h" | |
| #include "curl_printf.h" | |
| #include "strdup.h" | |
| @@ -3134,8 +3135,6 @@ sectransp_connect_common(struct Curl_easy *data, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = sectransp_recv; |
| - | conn->send[sockindex] = sectransp_send; |
| *done = TRUE; | |
| } | |
| else | |
| @@ -3530,7 +3529,9 @@ const struct Curl_ssl Curl_ssl_sectransp = { | |
| sectransp_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | sectransp_recv, /* recv decrypted data */ |
| + | sectransp_send, /* send data to encrypt */ |
| }; | |
| #ifdef __clang__ | |
| @@ -51,8 +51,10 @@ | |
| #endif | |
| #include "urldata.h" | |
| + | #include "cfilters.h" |
| #include "vtls.h" /* generic SSL protos etc */ | |
| + | #include "vtls_int.h" |
| #include "slist.h" | |
| #include "sendf.h" | |
| #include "strcase.h" | |
| @@ -295,6 +297,22 @@ static bool ssl_prefs_check(struct Curl_easy *data) | |
| static CURLcode | |
| ssl_connect_init_proxy(struct connectdata *conn, int sockindex) | |
| { | |
| + | /* TODO: curl has, so far, a limit of 2 SSL instances per |
| + | * connection per sockindex. One for SSL to the origin server |
| + | * and one for SSL to a proxy server. |
| + | * - conn->ssl[sockindex] |
| + | * - conn->ssl_proxy[sockindex] |
| + | * Any SSL activity always happens on conn->ssl[sockindex], even |
| + | * if it is to a proxy server. When the handshake is done, |
| + | * conn->bits.proxy_ssl_connected[sockindex] = TRUE |
| + | * is set, though conn->ssl_proxy[sockindex] is still empty. |
| + | * |
| + | * A new activity, on seeing this, will COPY the contents |
| + | * of conn->ssl[sockindex] to conn->ssl_proxy[sockindex] |
| + | * and NUL conn->ssl[sockindex] for a fresh start. |
| + | * |
| + | * This is what this function does. |
| + | */ |
| DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]); | |
| if(ssl_connection_complete == conn->ssl[sockindex].state && | |
| !conn->proxy_ssl[sockindex].use) { | |
| @@ -319,9 +337,8 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) | |
| } | |
| #endif | |
| - | CURLcode |
| - | Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex) |
| + | static CURLcode |
| + | ssl_connect(struct Curl_easy *data, struct connectdata *conn, int sockindex) |
| { | |
| CURLcode result; | |
| @@ -342,17 +359,19 @@ Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, | |
| result = Curl_ssl->connect_blocking(data, conn, sockindex); | |
| - | if(!result) |
| + | if(!result) { |
| Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ | |
| + | DEBUGASSERT(conn->ssl[sockindex].state == ssl_connection_complete); |
| + | } |
| else | |
| conn->ssl[sockindex].use = FALSE; | |
| return result; | |
| } | |
| - | CURLcode |
| - | Curl_ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, |
| - | bool isproxy, int sockindex, bool *done) |
| + | static CURLcode |
| + | ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, |
| + | bool isproxy, int sockindex, bool *done) |
| { | |
| CURLcode result; | |
| @@ -371,8 +390,11 @@ Curl_ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, | |
| result = Curl_ssl->connect_nonblocking(data, conn, sockindex, done); | |
| if(result) | |
| conn->ssl[sockindex].use = FALSE; | |
| - | else if(*done && !isproxy) |
| - | Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ |
| + | else if(*done) { |
| + | if(!isproxy) |
| + | Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ |
| + | DEBUGASSERT(conn->ssl[sockindex].state == ssl_connection_complete); |
| + | } |
| return result; | |
| } | |
| @@ -633,28 +655,6 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| - | void Curl_ssl_associate_conn(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | if(Curl_ssl->associate_connection) { |
| - | Curl_ssl->associate_connection(data, conn, FIRSTSOCKET); |
| - | if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && |
| - | conn->bits.sock_accepted) |
| - | Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET); |
| - | } |
| - | } |
| - | |
| - | void Curl_ssl_detach_conn(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | if(Curl_ssl->disassociate_connection) { |
| - | Curl_ssl->disassociate_connection(data, FIRSTSOCKET); |
| - | if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && |
| - | conn->bits.sock_accepted) |
| - | Curl_ssl->disassociate_connection(data, SECONDARYSOCKET); |
| - | } |
| - | } |
| - | |
| void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend) | |
| { | |
| if(Curl_ssl->free_multi_ssl_backend_data && mbackend) | |
| @@ -695,14 +695,6 @@ int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks) | |
| return GETSOCK_BLANK; | |
| } | |
| - | void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex) |
| - | { |
| - | DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); |
| - | Curl_ssl->close_one(data, conn, sockindex); |
| - | conn->ssl[sockindex].state = ssl_connection_none; |
| - | } |
| - | |
| CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, | |
| int sockindex) | |
| { | |
| @@ -712,9 +704,6 @@ CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, | |
| conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */ | |
| conn->ssl[sockindex].state = ssl_connection_none; | |
| - | conn->recv[sockindex] = Curl_recv_plain; |
| - | conn->send[sockindex] = Curl_send_plain; |
| - | |
| return CURLE_OK; | |
| } | |
| @@ -785,12 +774,6 @@ int Curl_ssl_check_cxn(struct connectdata *conn) | |
| return Curl_ssl->check_cxn(conn); | |
| } | |
| - | bool Curl_ssl_data_pending(const struct connectdata *conn, |
| - | int connindex) |
| - | { |
| - | return Curl_ssl->data_pending(conn, connindex); |
| - | } |
| - | |
| void Curl_ssl_free_certinfo(struct Curl_easy *data) | |
| { | |
| struct curl_certinfo *ci = &data->info.certs; | |
| @@ -1144,19 +1127,12 @@ bool Curl_ssl_cert_status_request(void) | |
| /* | |
| * Check whether the SSL backend supports false start. | |
| */ | |
| - | bool Curl_ssl_false_start(void) |
| + | bool Curl_ssl_false_start(struct Curl_easy *data) |
| { | |
| + | (void)data; |
| return Curl_ssl->false_start(); | |
| } | |
| - | /* |
| - | * Check whether the SSL backend supports setting TLS 1.3 cipher suites |
| - | */ |
| - | bool Curl_ssl_tls13_ciphersuites(void) |
| - | { |
| - | return Curl_ssl->supports & SSLSUPP_TLS13_CIPHERSUITES; |
| - | } |
| - | |
| /* | |
| * Default implementations for unsupported functions. | |
| */ | |
| @@ -1290,6 +1266,23 @@ static void multissl_close(struct Curl_easy *data, struct connectdata *conn, | |
| Curl_ssl->close_one(data, conn, sockindex); | |
| } | |
| + | static ssize_t multissl_recv_plain(struct Curl_easy *data, int sockindex, |
| + | char *buf, size_t len, CURLcode *code) |
| + | { |
| + | if(multissl_setup(NULL)) |
| + | return CURLE_FAILED_INIT; |
| + | return Curl_ssl->recv_plain(data, sockindex, buf, len, code); |
| + | } |
| + | |
| + | static ssize_t multissl_send_plain(struct Curl_easy *data, int sockindex, |
| + | const void *mem, size_t len, |
| + | CURLcode *code) |
| + | { |
| + | if(multissl_setup(NULL)) |
| + | return CURLE_FAILED_INIT; |
| + | return Curl_ssl->send_plain(data, sockindex, mem, len, code); |
| + | } |
| + | |
| static const struct Curl_ssl Curl_ssl_multi = { | |
| { CURLSSLBACKEND_NONE, "multi" }, /* info */ | |
| 0, /* supports nothing */ | |
| @@ -1317,7 +1310,9 @@ static const struct Curl_ssl Curl_ssl_multi = { | |
| NULL, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | multissl_recv_plain, /* recv decrypted data */ |
| + | multissl_send_plain, /* send data to encrypt */ |
| }; | |
| const struct Curl_ssl *Curl_ssl = | |
| @@ -1505,3 +1500,276 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, | |
| } | |
| #endif /* !USE_SSL */ | |
| + | |
| + | #ifdef USE_SSL |
| + | |
| + | static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) |
| + | { |
| + | /* 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. */ |
| + | Curl_ssl->close_one(data, cf->conn, cf->sockindex); |
| + | cf->conn->ssl[cf->sockindex].state = ssl_connection_none; |
| + | cf->conn->bits.proxy_ssl_connected[cf->sockindex] = FALSE; |
| + | cf->connected = FALSE; |
| + | } |
| + | |
| + | static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
| + | { |
| + | cf_close(cf, data); |
| + | } |
| + | |
| + | static CURLcode ssl_cf_setup(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | const struct Curl_dns_entry *remotehost) |
| + | { |
| + | CURLcode result; |
| + | |
| + | result = cf->next->cft->setup(cf->next, data, remotehost); |
| + | if(result) |
| + | return result; |
| + | |
| + | /* TODO our setup */ |
| + | return result; |
| + | } |
| + | |
| + | static void ssl_cf_close(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | cf_close(cf, data); |
| + | cf->next->cft->close(cf->next, data); |
| + | } |
| + | |
| + | static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool blocking, bool *done) |
| + | { |
| + | CURLcode result; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | result = cf->next->cft->connect(cf->next, data, blocking, done); |
| + | if(result || !*done) |
| + | return result; |
| + | |
| + | *done = FALSE; |
| + | if(blocking) { |
| + | result = ssl_connect(data, cf->conn, cf->sockindex); |
| + | *done = (result == CURLE_OK); |
| + | } |
| + | else { |
| + | result = ssl_connect_nonblocking(data, cf->conn, FALSE, |
| + | cf->sockindex, done); |
| + | } |
| + | if (*done) |
| + | cf->connected = TRUE; |
| + | return result; |
| + | } |
| + | |
| + | static CURLcode ssl_proxy_cf_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool blocking, bool *done) |
| + | { |
| + | CURLcode result; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | result = ssl_cf_connect(cf, data, blocking, done); |
| + | if(!result && *done) { |
| + | cf->conn->bits.proxy_ssl_connected[cf->sockindex] = TRUE; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | static bool ssl_cf_data_pending(struct Curl_cfilter *cf, |
| + | const struct Curl_easy *data) |
| + | { |
| + | if(Curl_ssl->data_pending(cf->conn, cf->sockindex)) |
| + | return TRUE; |
| + | return cf->next->cft->has_data_pending(cf->next, data); |
| + | } |
| + | |
| + | static ssize_t ssl_cf_send(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, const void *buf, size_t len, |
| + | CURLcode *err) |
| + | { |
| + | ssize_t nwritten; |
| + | |
| + | *err = CURLE_OK; |
| + | nwritten = Curl_ssl->send_plain(data, cf->sockindex, buf, len, err); |
| + | DEBUGF(infof(data, "cf_ssl_send(handle=%p, len=%ld) -> %ld, code=%d", |
| + | data, len, nwritten, *err)); |
| + | return nwritten; |
| + | } |
| + | |
| + | static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, char *buf, size_t len, |
| + | CURLcode *err) |
| + | { |
| + | ssize_t nread; |
| + | |
| + | *err = CURLE_OK; |
| + | nread = Curl_ssl->recv_plain(data, cf->sockindex, buf, len, err); |
| + | DEBUGF(infof(data, "cf_ssl_recv(handle=%p) -> %ld, code=%d", |
| + | data, nread, *err)); |
| + | return nread; |
| + | } |
| + | |
| + | static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | curl_socket_t *socks) |
| + | { |
| + | /* TODO, this needs to work for other than SOCKETFIRST filters |
| + | * and also nested filters. Needs change of implementations. |
| + | * What we really want to know if the SSL implementation wants |
| + | * to READ or WRITE or needs nothing. |
| + | */ |
| + | (void)data; |
| + | return Curl_ssl->getsock(cf->conn, socks); |
| + | } |
| + | |
| + | static void ssl_cf_def_attach_data(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | if(Curl_ssl->associate_connection) { |
| + | Curl_ssl->associate_connection(data, cf->conn, cf->sockindex); |
| + | } |
| + | } |
| + | |
| + | static void ssl_cf_def_detach_data(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | if(Curl_ssl->disassociate_connection) { |
| + | Curl_ssl->disassociate_connection(data, cf->sockindex); |
| + | } |
| + | } |
| + | |
| + | static const struct Curl_cftype cft_ssl = { |
| + | "SSL", |
| + | ssl_cf_destroy, |
| + | ssl_cf_def_attach_data, |
| + | ssl_cf_def_detach_data, |
| + | ssl_cf_setup, |
| + | ssl_cf_close, |
| + | ssl_cf_connect, |
| + | ssl_cf_get_select_socks, |
| + | ssl_cf_data_pending, |
| + | ssl_cf_send, |
| + | ssl_cf_recv, |
| + | }; |
| + | |
| + | static const struct Curl_cftype cft_ssl_proxy = { |
| + | "SSL-PROXY", |
| + | ssl_cf_destroy, |
| + | ssl_cf_def_attach_data, |
| + | ssl_cf_def_detach_data, |
| + | ssl_cf_setup, |
| + | ssl_cf_close, |
| + | ssl_proxy_cf_connect, |
| + | ssl_cf_get_select_socks, |
| + | ssl_cf_data_pending, |
| + | ssl_cf_send, |
| + | ssl_cf_recv, |
| + | }; |
| + | |
| + | CURLcode Curl_cfilter_ssl_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_ssl, NULL); |
| + | if(!result) |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | return result; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | CURLcode Curl_cfilter_ssl_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | result = Curl_cfilter_create(&cf, data, conn, sockindex, |
| + | &cft_ssl_proxy, NULL); |
| + | if(!result) |
| + | Curl_cfilter_add(data, conn, sockindex, cf); |
| + | return result; |
| + | } |
| + | |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| + | |
| + | size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data) |
| + | { |
| + | (void)data; |
| + | return Curl_ssl->sizeof_ssl_backend_data; |
| + | } |
| + | |
| + | bool Curl_ssl_supports(struct Curl_easy *data, int option) |
| + | { |
| + | (void)data; |
| + | return (Curl_ssl->supports & option)? TRUE : FALSE; |
| + | } |
| + | |
| + | void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, |
| + | CURLINFO info, int n) |
| + | { |
| + | struct connectdata *conn = data->conn; |
| + | |
| + | #if 0 |
| + | struct Curl_cfilter *cf = conn? conn->cfilters[sockindex] : NULL; |
| + | |
| + | for(; cf; cf = cf->next) { |
| + | if(cf->cft == cft_ssl || cf->cft == cft_ssl_proxy) { |
| + | if(n > 0) { |
| + | --n; |
| + | continue; |
| + | } |
| + | /* TODO: use cf->ctx instance once we have that */ |
| + | return Curl_ssl->get_internals(&data->conn->ssl[0], info); |
| + | } |
| + | } |
| + | #else |
| + | if(conn) { |
| + | size_t i; |
| + | (void)n; |
| + | (void)sockindex; |
| + | for(i = 0; i < (sizeof(conn->ssl) / sizeof(conn->ssl[0])); ++i) { |
| + | if(conn->ssl[i].use) { |
| + | return Curl_ssl->get_internals(&conn->ssl[i], info); |
| + | } |
| + | } |
| + | } |
| + | #endif |
| + | return NULL; |
| + | } |
| + | |
| + | bool Curl_ssl_use(struct connectdata *conn, int sockindex) |
| + | { |
| + | return conn->ssl[sockindex].use; |
| + | } |
| + | |
| + | bool Curl_cfilter_ssl_added(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; |
| + | |
| + | (void)data; |
| + | for(; cf; cf = cf->next) { |
| + | if(cf->cft == &cft_ssl) |
| + | return TRUE; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| + | #endif /* USE_SSL */ |
| @@ -27,6 +27,8 @@ | |
| struct connectdata; | |
| struct ssl_connect_data; | |
| + | struct ssl_primary_config; |
| + | struct Curl_ssl_session; |
| #define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ | |
| #define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ | |
| @@ -51,100 +53,9 @@ struct ssl_connect_data; | |
| backend */ | |
| struct multi_ssl_backend_data; | |
| - | struct Curl_ssl { |
| - | /* |
| - | * This *must* be the first entry to allow returning the list of available |
| - | * backends in curl_global_sslset(). |
| - | */ |
| - | curl_ssl_backend info; |
| - | unsigned int supports; /* bitfield, see above */ |
| - | size_t sizeof_ssl_backend_data; |
| - | |
| - | int (*init)(void); |
| - | void (*cleanup)(void); |
| - | |
| - | size_t (*version)(char *buffer, size_t size); |
| - | int (*check_cxn)(struct connectdata *cxn); |
| - | int (*shut_down)(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex); |
| - | bool (*data_pending)(const struct connectdata *conn, |
| - | int connindex); |
| - | |
| - | /* return 0 if a find random is filled in */ |
| - | CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, |
| - | size_t length); |
| - | bool (*cert_status_request)(void); |
| - | |
| - | CURLcode (*connect_blocking)(struct Curl_easy *data, |
| - | struct connectdata *conn, int sockindex); |
| - | CURLcode (*connect_nonblocking)(struct Curl_easy *data, |
| - | struct connectdata *conn, int sockindex, |
| - | bool *done); |
| - | |
| - | /* If the SSL backend wants to read or write on this connection during a |
| - | handshake, set socks[0] to the connection's FIRSTSOCKET, and return |
| - | a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or |
| - | GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. |
| - | Mandatory. */ |
| - | int (*getsock)(struct connectdata *conn, curl_socket_t *socks); |
| - | |
| - | void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); |
| - | void (*close_one)(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex); |
| - | void (*close_all)(struct Curl_easy *data); |
| - | void (*session_free)(void *ptr); |
| - | |
| - | CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); |
| - | CURLcode (*set_engine_default)(struct Curl_easy *data); |
| - | struct curl_slist *(*engines_list)(struct Curl_easy *data); |
| - | |
| - | bool (*false_start)(void); |
| - | CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, |
| - | unsigned char *sha256sum, size_t sha256sumlen); |
| - | |
| - | bool (*associate_connection)(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | int sockindex); |
| - | void (*disassociate_connection)(struct Curl_easy *data, int sockindex); |
| - | |
| - | void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend); |
| - | }; |
| - | |
| - | #ifdef USE_SSL |
| - | extern const struct Curl_ssl *Curl_ssl; |
| - | #endif |
| - | |
| - | int Curl_none_init(void); |
| - | void Curl_none_cleanup(void); |
| - | int Curl_none_shutdown(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex); |
| - | int Curl_none_check_cxn(struct connectdata *conn); |
| - | CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, |
| - | size_t length); |
| - | void Curl_none_close_all(struct Curl_easy *data); |
| - | void Curl_none_session_free(void *ptr); |
| - | bool Curl_none_data_pending(const struct connectdata *conn, int connindex); |
| - | bool Curl_none_cert_status_request(void); |
| - | CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); |
| - | CURLcode Curl_none_set_engine_default(struct Curl_easy *data); |
| - | struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); |
| - | bool Curl_none_false_start(void); |
| - | bool Curl_ssl_tls13_ciphersuites(void); |
| - | |
| CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, | |
| const curl_ssl_backend ***avail); | |
| - | #include "openssl.h" /* OpenSSL versions */ |
| - | #include "gtls.h" /* GnuTLS versions */ |
| - | #include "nssg.h" /* NSS versions */ |
| - | #include "gskit.h" /* Global Secure ToolKit versions */ |
| - | #include "wolfssl.h" /* wolfSSL versions */ |
| - | #include "schannel.h" /* Schannel SSPI version */ |
| - | #include "sectransp.h" /* SecureTransport (Darwin) version */ |
| - | #include "mbedtls.h" /* mbedTLS versions */ |
| - | #include "bearssl.h" /* BearSSL versions */ |
| - | #include "rustls.h" /* rustls versions */ |
| - | |
| #ifndef MAX_PINNED_PUBKEY_SIZE | |
| #define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ | |
| #endif | |
| @@ -200,29 +111,15 @@ bool Curl_ssl_config_matches(struct ssl_primary_config *data, | |
| bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, | |
| struct ssl_primary_config *dest); | |
| void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); | |
| - | /* An implementation of the getsock field of Curl_ssl that relies |
| - | on the ssl_connect_state enum. Asks for read or write depending |
| - | on whether conn->state is ssl_connect_2_reading or |
| - | ssl_connect_2_writing. */ |
| - | int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks); |
| curl_sslbackend Curl_ssl_backend(void); | |
| #ifdef USE_SSL | |
| int Curl_ssl_init(void); | |
| void Curl_ssl_cleanup(void); | |
| - | CURLcode Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex); |
| - | CURLcode Curl_ssl_connect_nonblocking(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | bool isproxy, |
| - | int sockindex, |
| - | bool *done); |
| /* tell the SSL stuff to close down all open information regarding | |
| connections (and thus session ID caching etc) */ | |
| void Curl_ssl_close_all(struct Curl_easy *data); | |
| - | void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, |
| - | int sockindex); |
| CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, | |
| int sockindex); | |
| CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); | |
| @@ -233,8 +130,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); | |
| - | bool Curl_ssl_data_pending(const struct connectdata *conn, |
| - | int connindex); |
| int Curl_ssl_check_cxn(struct connectdata *conn); | |
| /* Certificate information list handling. */ | |
| @@ -310,43 +205,71 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, | |
| bool Curl_ssl_cert_status_request(void); | |
| - | bool Curl_ssl_false_start(void); |
| - | |
| - | void Curl_ssl_associate_conn(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | void Curl_ssl_detach_conn(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| + | bool Curl_ssl_false_start(struct Curl_easy *data); |
| void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend); | |
| #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ | |
| + | CURLcode Curl_cfilter_ssl_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | CURLcode Curl_cfilter_ssl_proxy_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| + | |
| + | bool Curl_cfilter_ssl_added(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | |
| + | /** |
| + | * True iff the underlying SSL implementation supports the option. |
| + | * Option is one of the defined SSLSUPP_* values. |
| + | * `data` maybe NULL for the features of the default implementation. |
| + | */ |
| + | bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); |
| + | |
| + | /** |
| + | * Get the internal ssl instance (like OpenSSL's SSL*) from the filter |
| + | * chain at `sockindex` of type specified by `info`. |
| + | * For `n` == 0, the first active (top down) instance is returned. |
| + | * 1 gives the second active, etc. |
| + | * NULL is returned when no active SSL filter is present. |
| + | */ |
| + | void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, |
| + | CURLINFO info, int n); |
| + | |
| + | size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data); |
| + | |
| + | bool Curl_ssl_use(struct connectdata *conn, int sockindex); |
| + | |
| #else /* if not USE_SSL */ | |
| /* When SSL support is not present, just define away these function calls */ | |
| #define Curl_ssl_init() 1 | |
| #define Curl_ssl_cleanup() Curl_nop_stmt | |
| - | #define Curl_ssl_connect(x,y,z) CURLE_NOT_BUILT_IN |
| #define Curl_ssl_close_all(x) Curl_nop_stmt | |
| - | #define Curl_ssl_close(x,y,z) Curl_nop_stmt |
| #define Curl_ssl_shutdown(x,y,z) CURLE_NOT_BUILT_IN | |
| #define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN | |
| #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN | |
| #define Curl_ssl_engines_list(x) NULL | |
| - | #define Curl_ssl_send(a,b,c,d,e) -1 |
| - | #define Curl_ssl_recv(a,b,c,d,e) -1 |
| #define Curl_ssl_initsessions(x,y) CURLE_OK | |
| - | #define Curl_ssl_data_pending(x,y) 0 |
| #define Curl_ssl_check_cxn(x) 0 | |
| #define Curl_ssl_free_certinfo(x) Curl_nop_stmt | |
| - | #define Curl_ssl_connect_nonblocking(x,y,z,w,a) CURLE_NOT_BUILT_IN |
| #define Curl_ssl_kill_session(x) Curl_nop_stmt | |
| #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) | |
| #define Curl_ssl_cert_status_request() FALSE | |
| - | #define Curl_ssl_false_start() FALSE |
| - | #define Curl_ssl_tls13_ciphersuites() FALSE |
| - | #define Curl_ssl_associate_conn(a,b) Curl_nop_stmt |
| - | #define Curl_ssl_detach_conn(a,b) Curl_nop_stmt |
| + | #define Curl_ssl_false_start(a) FALSE |
| + | #define Curl_ssl_get_internals(a,b,c,d) NULL |
| + | #define Curl_ssl_supports(a,b) FALSE |
| + | #define Curl_ssl_get_backend_data_size(a) 0 |
| + | #define Curl_ssl_use(a,b) FALSE |
| + | #define Curl_cfilter_ssl_added(a,b,c) FALSE |
| + | #define Curl_cfilter_ssl_add(a,b,c) CURLE_NOT_BUILT_IN |
| + | #define Curl_cfilter_ssl_proxy_add(a,b,c) CURLE_NOT_BUILT_IN |
| #endif | |
| #endif /* HEADER_CURL_VTLS_H */ | |
| @@ -0,0 +1,127 @@ | |
| + | #ifndef HEADER_CURL_VTLS_INT_H |
| + | #define HEADER_CURL_VTLS_INT_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "curl_setup.h" |
| + | #include "urldata.h" |
| + | |
| + | /* Definitions for SSL Implementations */ |
| + | |
| + | struct Curl_ssl { |
| + | /* |
| + | * This *must* be the first entry to allow returning the list of available |
| + | * backends in curl_global_sslset(). |
| + | */ |
| + | curl_ssl_backend info; |
| + | unsigned int supports; /* bitfield, see above */ |
| + | size_t sizeof_ssl_backend_data; |
| + | |
| + | int (*init)(void); |
| + | void (*cleanup)(void); |
| + | |
| + | size_t (*version)(char *buffer, size_t size); |
| + | int (*check_cxn)(struct connectdata *cxn); |
| + | int (*shut_down)(struct Curl_easy *data, struct connectdata *conn, |
| + | int sockindex); |
| + | bool (*data_pending)(const struct connectdata *conn, |
| + | int connindex); |
| + | |
| + | /* return 0 if a find random is filled in */ |
| + | CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, |
| + | size_t length); |
| + | bool (*cert_status_request)(void); |
| + | |
| + | CURLcode (*connect_blocking)(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex); |
| + | CURLcode (*connect_nonblocking)(struct Curl_easy *data, |
| + | struct connectdata *conn, int sockindex, |
| + | bool *done); |
| + | |
| + | /* If the SSL backend wants to read or write on this connection during a |
| + | handshake, set socks[0] to the connection's FIRSTSOCKET, and return |
| + | a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or |
| + | GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. |
| + | Mandatory. */ |
| + | int (*getsock)(struct connectdata *conn, curl_socket_t *socks); |
| + | |
| + | void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); |
| + | void (*close_one)(struct Curl_easy *data, struct connectdata *conn, |
| + | int sockindex); |
| + | void (*close_all)(struct Curl_easy *data); |
| + | void (*session_free)(void *ptr); |
| + | |
| + | CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); |
| + | CURLcode (*set_engine_default)(struct Curl_easy *data); |
| + | struct curl_slist *(*engines_list)(struct Curl_easy *data); |
| + | |
| + | bool (*false_start)(void); |
| + | CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, |
| + | unsigned char *sha256sum, size_t sha256sumlen); |
| + | |
| + | bool (*associate_connection)(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex); |
| + | void (*disassociate_connection)(struct Curl_easy *data, int sockindex); |
| + | |
| + | void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend); |
| + | |
| + | ssize_t (*recv_plain)(struct Curl_easy *data, int sockindex, char *buf, |
| + | size_t len, CURLcode *code); |
| + | ssize_t (*send_plain)(struct Curl_easy *data, int sockindex, |
| + | const void *mem, size_t len, CURLcode *code); |
| + | |
| + | }; |
| + | |
| + | extern const struct Curl_ssl *Curl_ssl; |
| + | |
| + | |
| + | int Curl_none_init(void); |
| + | void Curl_none_cleanup(void); |
| + | int Curl_none_shutdown(struct Curl_easy *data, struct connectdata *conn, |
| + | int sockindex); |
| + | int Curl_none_check_cxn(struct connectdata *conn); |
| + | CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, |
| + | size_t length); |
| + | void Curl_none_close_all(struct Curl_easy *data); |
| + | void Curl_none_session_free(void *ptr); |
| + | bool Curl_none_data_pending(const struct connectdata *conn, int connindex); |
| + | bool Curl_none_cert_status_request(void); |
| + | CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); |
| + | CURLcode Curl_none_set_engine_default(struct Curl_easy *data); |
| + | struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); |
| + | bool Curl_none_false_start(void); |
| + | int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks); |
| + | |
| + | #include "openssl.h" /* OpenSSL versions */ |
| + | #include "gtls.h" /* GnuTLS versions */ |
| + | #include "nssg.h" /* NSS versions */ |
| + | #include "gskit.h" /* Global Secure ToolKit versions */ |
| + | #include "wolfssl.h" /* wolfSSL versions */ |
| + | #include "schannel.h" /* Schannel SSPI version */ |
| + | #include "sectransp.h" /* SecureTransport (Darwin) version */ |
| + | #include "mbedtls.h" /* mbedTLS versions */ |
| + | #include "bearssl.h" /* BearSSL versions */ |
| + | #include "rustls.h" /* rustls versions */ |
| + | |
| + | #endif /* HEADER_CURL_VTLS_INT_H */ |
| @@ -55,6 +55,7 @@ | |
| #include "sendf.h" | |
| #include "inet_pton.h" | |
| #include "vtls.h" | |
| + | #include "vtls_int.h" |
| #include "keylog.h" | |
| #include "parsedate.h" | |
| #include "connect.h" /* for the connect timeout */ | |
| @@ -606,9 +607,6 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, | |
| ERR_clear_error(); | |
| - | conn->recv[sockindex] = wolfssl_recv; |
| - | conn->send[sockindex] = wolfssl_send; |
| - | |
| /* Enable RFC2818 checks */ | |
| if(SSL_CONN_CONFIG(verifyhost)) { | |
| char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); | |
| @@ -1135,8 +1133,6 @@ wolfssl_connect_common(struct Curl_easy *data, | |
| if(ssl_connect_done == connssl->connecting_state) { | |
| connssl->state = ssl_connection_complete; | |
| - | conn->recv[sockindex] = wolfssl_recv; |
| - | conn->send[sockindex] = wolfssl_send; |
| *done = TRUE; | |
| } | |
| else | |
| @@ -1242,7 +1238,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = { | |
| wolfssl_sha256sum, /* sha256sum */ | |
| NULL, /* associate_connection */ | |
| NULL, /* disassociate_connection */ | |
| - | NULL /* free_multi_ssl_backend_data */ |
| + | NULL, /* free_multi_ssl_backend_data */ |
| + | wolfssl_recv, /* recv decrypted data */ |
| + | wolfssl_send, /* send data to encrypt */ |
| }; | |
| #endif | |